Пока числа в вашем примере не содержат разделитель, который 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
, второй столбец с разделителями~
-которого не является ключом в этом хеше.
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 -
).
Дело в том, что на самом деле не существует такого понятия, как «состояние EOF». По соглашению чтение, возвращающее 0, интерпретируется как конец файла, но, как вы видели, вы можете продолжать чтение. Дополнительные чтения могут снова вернуть 0 или они могут вернуть больше данных, если какой-то другой процесс за это время записал в файл.
Даже Ctrl-D
от терминала не имеет специального свойства сигнализации "EOF". Что он действительно делает, так это возвращает текущую строку ввода в процесс, точно так же, как нажатие клавиши Enter, но с той разницей, что новая строка не добавляется. Если Ctrl -D нажата как первый ввод в строке, т. е. пустая строка, то возвращается ноль символов, что, опять же, по соглашению, интерпретируется как «EOF».
Процесс, вызвавший системный вызов чтения, сам определяет, как интерпретировать значение 0, возвращаемое функцией чтения.