list=/errors_exception.txt
cd /test
while IFS= read -r pattern ; do
for file in * ; do
if zcat < "$file" | grep -Fxq "$pattern"; then
echo "$pattern found pattern in $file"
fi
done
done <"$list" > output
Примечания:
Ни одна из двух приведенных ниже строк не сделает то, что вы ожидаете:
для ШАБЛОНА в `cat $ LIST`
для ФАЙЛА в $ (ls)
В обоих случаях оболочка выполняет разделение слов, которое вам не нужно. Предлагаемый выше код позволяет избежать этого.
Действительно ли файл errors_exception.txt
находится в корневом каталоге?
Я преобразовал переменные в нижний регистр. Это соглашение для переменных, создаваемых пользователем. Это соглашение предотвратит случайную перезапись некоторых критических параметров оболочки.
Когда оболочка выполняет:
for PATTERN in `cat $LIST`
она запускает cat $ LIST
. Когда это происходит, пробелы, табуляции и возврат каретки обрабатываются как одно и то же: разрыв слова. Таким образом, фактически, после разделения слов эта строка становится:
for PATTERN in one one two three four five six
и, когда выполняется цикл for
, ШАБЛОН
последовательно назначается одному, одному, двум, трем, четырем , пять и шесть.
На самом деле вам нужно, чтобы каждая строка рассматривалась как строка. Вот почему вместо этого используется конструкция while read .... done <"$ list"
: в каждом цикле она считывает одну целую строку.
Та же проблема может произойти с этой строкой, если в именах файлов есть пробелы:
for FILE in $(ls)
Результаты ls
подставляются в строку и, если в именах файлов есть пробелы, табуляции или в них выполняется возврат каретки (все это допустимые символы), затем имена разбиваются на части. Например, в пустом каталоге создайте один файл:
$ touch "a b c"
Теперь запустите цикл for
:
$ for file in $(ls); do echo $file; done
a
b
c
Циклы for
запускаются три раза, хотя существует только один файл. Это связано с тем, что в имени файла есть пробелы, и после разделения слов цикл for
получает три аргумента: a, b и c.
Этого легко избежать. Вместо этого используйте:
for file in *
Оболочка достаточно умен, чтобы сохранять каждое имя файла здесь, независимо от того, какие символы в его имени.
Если мы также хотим искать в подкаталогах сжатые файлы, мы можем использовать функцию globstar в bash следующим образом:
list=/errors_exception.txt
cd /test
shopt -s globstar
while IFS= read -r pattern ; do
for file in **/*.gz ; do
if zcat < "$file" | grep -Fxq "$pattern"; then
echo "$pattern found pattern in $file"
fi
done
done <"$list" > output
Для этого требуется bash
.
Пусть ваша оболочка расширит переменную с помощью ", а не '.
Пример:
victor@pyfg:~$ line_number=2
victor@pyfg:~$ sed -n "${line_number},${line_number}p" /etc/hosts
1.2.3.4 row-2
Так как вы распечатываете только одну строку, вы можете так же:
victor@pyfg:~$ sed -n "${line_number}p" /etc/hosts
1.2.3.4 row-2
Наверное, в вашем примере будет лучше, если после того, как вы найдете распечатанные данные, вы захотите, чтобы собеседник q
uit входного файла нашел нужные вам данные. Например:
sed 1q <INPUT
Это позволит автоматически распечатать первую строку, а затем полностью завершить процесс. Вы также можете сделать:
head -n1 <INPUT
... для достижения того же эффекта. Однако,
sed
также интерпретирует адрес,диапазоны
, и это могут быть /regex/,/addresses/
или комбинации двух типов. Например:
sed '9q;/LINE[0-9]/,/^$/!d' <<FILE
$(printf 'LINE %s\nLINE%s\nLINE %s\\nNOT LINE %s\n\n' $(seq 32))
FILE
###OUTPUT###
LINE2
LINE 3\nNOT LINE 4
LINE6
LINE 7\nNOT LINE 8
LINE 9
В приведенном выше примере sed
d
поднимает любую встреченную строку, которая делает !
не происходит между строкой, содержащей строку LINE
, за которой следует [0-9]
- или любой числовой символ - и строкой, не содержащей /^$/
вообще никаких символов - или пустой строкой. Таким образом, строки 1 и 5 - илиLINE [15]
удаляются из вывода. И наоборот, LINE 9
распечатывается, потому что на 9-ой строке задано q
, так что на этом этапе он прекращает обработку своего скрипта - что происходит перед фильтром, который его удалит.
Как было предложено в другом ответе, работа с параметрами переменных производится так же просто, как и разрешение оболочке расширить их для вас - для этого следует использовать "$parameter". Важно помнить, что недействительные входные данные будут рассматриваться как таковые. Например, по той же причине:
echo "///" | sed 's////g'
sed: -e expression #1, char 5: unknown option to `s'
... также:
v=/ ;echo "///" | sed "s/$v//g" :(
sed: -e expression #1, char 5: unknown option to `s'
Значение каждой переменной должно быть корректным скриптом sed
и не может содержать разделительных символов.
просто для удовольствия, решение для awk:
awk -v line=2 'FNR==line' /etc/hosts