grep файлы из списка

14

Я пытаюсь запустить grep для списка из нескольких сотен файлов:

$ head -n 3 <(cat files.txt)
admin.php
ajax/accept.php
ajax/add_note.php

Однако, несмотря на то, что я искал строку, которая, как мне известно, находится в файлах, следующее не ищет файлы:

$ grep -i 'foo' <(cat files.txt)

$ grep -i 'foo' admin.php
The foo was found

Я знаком с -fфлагом, который будет читать шаблоны из файла. Но как читать входные файлы ?

Я рассмотрел ужасный обходной путь копирования файлов во временный каталог, который, cpкажется, поддерживает <(cat files.txt)формат, и оттуда копировал файлы. Ширли, есть лучший способ.

dotancohen
источник

Ответы:

22

Вы, кажется, сгребаете список имен файлов, а не сами файлы. <(cat files.txt)просто перечисляет файлы. Попробуйте <(cat $(cat files.txt))на самом деле объединить их и искать их как один поток, или

grep -i 'foo' $(cat files.txt)

чтобы дать grep все файлы.

Однако, если в списке слишком много файлов, у вас могут возникнуть проблемы с количеством аргументов. В таком случае я бы просто написал

while read filename; do grep -Hi 'foo' "$filename"; done < files.txt
Орион
источник
Спасибо! Я не осознавал, что whileсмог получить строки file.txt как таковые.
Dotancohen
Здесь вы захотите отключить глобальную часть этого оператора split + glob (если оболочка не zsh).
Стефан Шазелас
1
whileне совсем получает строки из файла, readделает это; whileпросто позволяет нам сделать это в цикле. Цикл заканчивается, когда происходит readсбой (т. Е. Возвращает ненулевой код возврата), обычно из-за достижения конца файла.
PM 2Ring 13.01.15
1
Чтобы прочитать (текстовую) строку, синтаксис IFS= read -r filename, read filenameэто что-то еще.
Стефан Шазелас
1
Обратите внимание, что -Hэто расширение GNU. Вы скучаете по некоторым --.
Стефан Шазелас
8
xargs grep -i -- foo /dev/null < files.txt

при условии, что файлы пустые или разделены символом новой строки (где кавычки или обратные слэши могут использоваться для экранирования этих разделителей). В GNU xargsвы можете указать разделитель с помощью -d(который затем отключает обработку цитирования).

(unset -v IFS; set -f; grep -i -- foo $(cat files.txt))

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

Они также предполагают, что ни один из файлов не вызывается -.

Стефан Шазелас
источник
Лучше / быстрее использовать $(< file)вместо $(cat file), по крайней мере в bashи zsh.
Джимми
7

Чтобы прочитать список имен файлов из stdin вы можете использовать xargs. Например,

cat files.txt | xargs -d'\n' grep -i -- 'foo'

По умолчанию xargsсчитывает элементы из стандартного ввода, разделенные пробелами. Он -d'\n'говорит ему использовать символ новой строки в качестве разделителя аргументов, чтобы он мог обрабатывать имена файлов, содержащие пробелы. (Как указывает Стефан Шазелас, это расширение GNU). Тем не менее, он не справится с именами файлов, содержащих переводы строк; нам понадобится немного более сложный подход, чтобы справиться с этим.

FWIW, этот подход несколько быстрее, чем while readцикл, так как readкоманда bash очень медленная - она ​​читает свои данные символ за символом, тогда как xargsсчитывает ввод более эффективно. Кроме того, команда xargsвызывается только grepстолько раз, сколько необходимо, при этом каждый вызов получает несколько имен файлов, и это более эффективно, чем grepиндивидуальный вызов для каждого имени файла.

Для получения дополнительной информации см. Справочную страницу xargs и информационную страницу xargs.

PM 2Ring
источник
3

xargsможете читать элементы из файла (например, из вашего files.txtсписка) с помощью этой опции:

   --arg-file=file
   -a file
          Read items from file instead of standard input.  If you use this
          option, stdin remains unchanged when commands are  run.   Other
          wise, stdin is redirected from /dev/null.

Так что это тоже должно работать:

xargs -a files.txt grep -i 'foo'

или для пробелов в именах файлов

xargs -d'\n' -a files.txt grep -i 'foo'
xargs -I{} -a files.txt grep -i 'foo' {}
Xen2050
источник
1

Вы также можете сделать для, но пример Ориона является самым простым:

for i in $(cat files.txt); do grep -i 'foo' $i ; done

(Для каждого файла, указанного в файле files.txt, выполните для него команду grep.)

Майкл
источник