как grep большое количество файлов?

Я пытаюсь grep 40к файлов в текущем каталоге и я получаю эту ошибку.

for i in $(cat A01/genes.txt); do grep $i *.kaks; done > A01/A01.result.txt
-bash: /usr/bin/grep: Argument list too long

как обычно grep тысяч файлов?

спасибо Upendra

5 ответов


это огорчает Дэвида...

все до сих пор неправильно (за исключением Анубхава).

Shell scripting не похож на любой другой язык программирования, потому что большая часть интерпретации строк происходит от силы оболочки, интерполирующей их до фактического выполнения команды.

давайте возьмем что-то простое:

$ set -x
$ ls
+ ls
bar.txt foo.txt fubar.log
$ echo The text files are *.txt
echo The text files are *.txt
> echo The text files are bar.txt foo.txt
The text files are bar.txt foo.txt
$ set +x
$

на set -x позволяет увидеть, как оболочка фактически интерполирует глобус, а затем возвращает это команде в качестве входных данных. The > указывает на строку, которая фактически выполняется командой.

видно, что echo команда не интерпретирует *. Вместо этого оболочка захватывает * и заменяет его именами соответствующих файлов. Тогда и только тогда echo команда фактически выполняет команду.

когда у вас есть 40K плюс файлы, и вы делаете grep *, вы расширяете, что * С именами эти 40,000 плюс файлы перед grep даже есть шанс выполнить, а вот где сообщение об ошибке / usr/bin / grep: список аргументов слишком длинный откуда.

к счастью, Unix имеет способ обойти эту дилемму:

$ find . -name "*.kaks" -type f -maxdepth 1 | xargs grep -f A01/genes.txt

на find . -name "*.kaks" -type f -maxdepth 1 найдет все ваши *.kaks файлы и -depth 1 будет содержать только файлы в текущем каталоге. The -type f убедитесь, что вы только забрать файлы, а не каталог.

на find команда передает имена файлов в xargs и xargs добавит имена файла к . Однако,xargs имеет трюк в рукаве. Он знает, как долго буфер командной строки, и выполнит grep когда буфер командной строки заполнен, затем передайте другую серию файлов в grep. Сюда,grep выполняется, возможно, три или десять раз (в зависимости от размера буфера командной строки), и все наши файлы используемый.

к сожалению, xargs использует пробелы в качестве разделителя для имен файлов. Если ваши файлы содержат пробелы или вкладки, у вас возникнут проблемы с xargs. К счастью, есть еще одно исправление:

$ find . -name "*.kaks" -type f -maxdepth 1 -print0 | xargs -0 grep -f A01/genes.txt

на -print0 вызывает find чтобы распечатать имена файлов, разделенных не новыми строками,а символом NUL. The на xargs говорит xargs что разделитель файлов - это не пробел, а символ NUL. Таким образом, фиксирует вопрос.

вы также можете сделать это:

$ find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \;

это выполнит grep для каждого файла, найденного вместо того, что xargs и только grep для всех файлов, которые он может заполнить в командной строке. Преимущество этого заключается в том, что он полностью избегает вмешательства оболочки. Однако она может быть или не быть менее эффективной.

было бы интересно поэкспериментировать и посмотреть, какой из них более эффективен. Вы можете использовать time в см.:

$ time find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \;

это выполнит команду, а затем скажет вам, сколько времени это заняло. Попробуйте это с -exec и xargs и посмотреть, что быстрее. Дайте нам знать, что вы ищите.


вы можете комбинировать find С grep такой:

find . -maxdepth 1 -name '*.kaks' -exec grep -H -f A01/genes.txt '{}' \; > A01/A01.result.txt

вы можете использовать рекурсивную функцию grep:

for i in $(cat A01/genes.txt); do 
    grep -r $i .
done > A01/A01.result.txt

хотя, если вы хотите выбрать только kaks файлы:

for i in $(cat A01/genes.txt); do 
    find . -iregex '.*\.kaks$' -exec grep $i \;
done > A01/A01.result.txt

поместите еще один цикл for внутри внешнего:

for f in *.kaks; do
   grep -H  $i "$f"
done

кстати, вы заинтересованы в поиске каждого вхождения в каждом файле или просто если строка поиска существует там один или несколько раз? Если это" достаточно хорошо", чтобы знать, что строка происходит там один или несколько раз, вы можете указать"- n 1 " grep, и он не будет беспокоиться о чтении/поиске остальной части файла после поиска первого совпадения, что потенциально может сэкономить много времени.


следующее решение сработало для меня:

:
 grep -r "example\.com" *
 -bash: /bin/grep: Argument list too long

устранение:

grep -r "example\.com" .

["в более новых версиях grep вы можете опустить".- как и следует из текущего справочника."]

источник: Reinlick, J. https://www.saotn.org/bash-grep-through-large-number-files-argument-list-too-long/