как 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/