Как отправить EOF в дескриптор файла (оставив его открытым)?

Пока числа в вашем примере не содержат разделитель, который sedиспользует (по умолчанию /), $pв вашем коде будет интерпретироваться как регулярное выражение (со всеми что это значит ).

Ваш код:

search_dir='dummy'
filename='numbers.txt'

for entry in "$search_dir"/*
do
  while read p;
  do 
    sed -i '' "/$p/d" $entry
  done < $filename
done

Здесь вы хотите удалить все строки в файлах под $search_dir, которые содержат любое из чисел в $filename. Работает это или нет, зависит от того, как ваш sedотносится к -i ''. В некоторых реализациях sedвам пришлось бы использовать -iбез аргумента.

Относится к sed -iи переносимости:Как добиться переносимости с помощью sed -i (в -редактирование места )?

Безопаснее записать результат во временный файл, а затем переместить этот файл в исходное имя:

for entry in "$search_dir"/*
do
  while read p;
  do 
    sed "/$p/d" "$entry" >"$entry.tmp" && mv "$entry.tmp" "$entry"
  done <"$filename"
done

Это гарантирует, что он будет работать независимо от sedреализации, с которой вы работаете. В общем, это плохая идея - пытаться внести изменения в файлы в -при тестировании скрипта, поэтому вы можете закомментировать это mv, прежде чем вы будете довольны тем, как скрипт работает в остальном.

Это все еще немного небезопасно в качестве общего решения, хотя, поскольку вы на самом деле «используете данные как код» (, числа являются данными, и вы используете их как часть своего sedскрипта ). Это означает, что вы легко можете вызвать синтаксическую ошибку в сценарии sed, просто вставив /в одно из чисел в вашем файле чисел.

Так как операция настолько проста, мы можем вместо этого использовать grep.Это также избавляет от внутреннего цикла while:

for entry in "$search_dir"/*
do
  grep -Fv -f "$filename" "$entry" >"$entry.tmp" && mv "$entry.tmp" "$entry"
done

Это заставит grepпрочитать свои шаблоны из $filenameи применить их к файлу $entry. -vозначает, что мы будем отбрасывать любую строку, содержащую шаблон, а -Fозначает, что grepбудет не интерпретировать числа как регулярные выражения, а как фиксированные строки. С помощью -f "$filename"мы получаем grepдля чтения строк из $filename.

Если в разделе $search_dirмогут быть каталоги, мы хотели бы пропустить эти:

for entry in "$search_dir"/*
do
  [ ! -f "$entry" ] && continue
  grep -Fv -f "$filename" "$entry" >"$entry.tmp" && mv "$entry.tmp" "$entry"
done

Другой, еще более безопасный способ выполнения операции — использовать awk. Поскольку в решениях sedи grepвыше число соответствует в любом месте в строке, вполне возможно, что мы могли бы удалить неправильные строки. С awkлегко сопоставить только второе~-поле с разделителями в данных:

for entry in "$search_dir"/*; do
    [ ! -f "$entry" ] && continue
    awk -F '~' 'NR==FNR { num[$0]; next } !($2 in num)' "$filename" "$entry" >"$entry.tmp" &&
    mv "$entry.tmp" "$entry"
done

Программа awkсначала заполняет ассоциативный массив/хэш числами в качестве ключей, а затем печатает каждую строку из файла $entry, второй столбец с разделителями~-которого не является ключом в этом хеше.

0
25.05.2020, 02:10
2 ответа

Ctrl -D (0x04 )сопоставляется с EOF терминальным устройством. Если вы читаете из канала или файла, этого не произойдет.

echo -e "a\x04b" | cat

или

echo -e "a\x04b" | hd

Если первая программа в конвейере читает с терминала и вы хотите игнорировать сопоставленный EOF, вы можете, например, изменить поведение терминала:

stty eof -

И Ctrl -D больше не будет работать. Или вы можете изменить дисциплину всей линии с помощью:

stty raw

И читать его так же, как и файл, без преобразований.

Вы можете использовать символ EOF для отправки нескольких файлов при условии, что ни один из этих файлов не содержит этот символ. Пти сделал бы это.

Но, возможно, вам лучше использовать готовые решения, такие как MIME multipart, такие вещи, как zmodem,или архивация в канал (, например, здесь второй tarперечисляет файлы, которые были заархивированы первым:tar cf - *txt | tar tf -).

1
18.03.2021, 23:33

Дело в том, что на самом деле не существует такого понятия, как «состояние EOF». По соглашению чтение, возвращающее 0, интерпретируется как конец файла, но, как вы видели, вы можете продолжать чтение. Дополнительные чтения могут снова вернуть 0 или они могут вернуть больше данных, если какой-то другой процесс за это время записал в файл.

Даже Ctrl-Dот терминала не имеет специального свойства сигнализации "EOF". Что он действительно делает, так это возвращает текущую строку ввода в процесс, точно так же, как нажатие клавиши Enter, но с той разницей, что новая строка не добавляется. Если Ctrl -D нажата как первый ввод в строке, т. е. пустая строка, то возвращается ноль символов, что, опять же, по соглашению, интерпретируется как «EOF».

Процесс, вызвавший системный вызов чтения, сам определяет, как интерпретировать значение 0, возвращаемое функцией чтения.

2
18.03.2021, 23:33

Теги

Похожие вопросы