выход ограничения на выход И избежать сигнала 13

У меня есть каталог с ~ 1M файлами и вам нужно искать определенные шаблоны. Я знаю, как это сделать для всех файлов:

find /path/ -exec grep -H -m 1 'pattern' \{\} \; 

Полная мощность не требуется (слишком медленно). Несколько первых ударов в порядке, поэтому я попытался ограничить количество строк:

 find /path/ -exec grep -H -m 1 'pattern' \{\} \; | head -n 5 

Это приводит к 5 линиям, за которыми следует

 find: `grep' terminated by signal 13 

и find продолжает работать. Это хорошо объяснено здесь . Я попытался quit действие:

 find /path/ -exec grep -H -m 1 'pattern' \{\} \; -quit 

Это выводит только первое совпадение.

Можно ли ограничить вывод вывода конкретным количеством результатов (например, предоставить аргумент для quit аналогичный head -n )?

Поскольку вы уже используете расширения GNU ( -quit , -H , -m1 ), вы можете также использовать параметр -r GNU grep вместе с --line-buffered чтобы он --line-buffered совпадения, как только они были найдены , поэтому он скорее всего будет убит SIGPIPE, как только он напишет 6-ю строчку:

 grep -rHm1 --line-buffered pattern /path | head -n 5 

С помощью find вам, вероятно, нужно будет сделать что-то вроде:

 find /path -type f -exec sh -c ' grep -Hm1 --line-buffered pattern "$@" [ "$(kill -l "$?")" = PIPE ] && kill -s PIPE "$PPID" ' sh {} + | head -n 5 

То есть, завершите grep в sh (вы все равно хотите запустить как можно меньше grep вызовов, следовательно {} + ) и убейте своего родителя ( find ), когда grep умирает SIGPIPE.

Другим подходом может быть использование xargs в качестве альтернативы -exec {} + . xargs выходит сразу, когда команда xargs сигнала, поэтому в:

  find . -type f -print0 | xargs -r0 grep -Hm1 --line-buffered pattern | head -n 5 

( -r и -0 – расширения GNU). Как только grep пишет xargs трубе, как grep и xargs выйдут, и find выйдет, а в следующий раз после чего что-то напечатает. Запуск find под stdbuf -oL может сделать это раньше.

Версия POSIX может быть:

 trap - PIPE # restore default SIGPIPE handler in case it was disabled RE=pattern find /path -type f -exec sh -c ' for file do awk '\'' $0 ~ ENVIRON["RE"] { print FILENAME ": " $0 exit }'\'' < "$file" if [ "$(kill -l "$?")" = PIPE ]; then kill -s PIPE "$PPID" exit fi done' sh {} + | head -n 5 

Очень неэффективен, поскольку он запускает несколько команд для каждого файла.

Решение избежать ошибок может быть следующим:

 find / -type f -print0 \ | xargs -0 -L 1 grep -H -m 1 --line-buffered 2>/dev/null \ | head -10 

В этом примере xargs останавливается после сбоя команды, поэтому будет только одна ошибка в канале, которая будет фильтроваться перенаправлением stderr.

Вы одновременно grep один файл. С вашим -quit вы остановите поиск на первом успешном grep.

[update] Мое первое решение заключалось в том, чтобы сразу grep несколько файлов:

 find /path/ -type f -exec grep -H -m 1 'pattern' \{\} + -quit | head -n 5 

(магия находится в + в конце -exec . Добавлен -type f . Вы можете удалить параметр -H для grep если вы уверены, что / path / содержит несколько файлов)

Проблема здесь, как сообщает @ StéphaneChazelas, заключается в том, что команда -exec выполняется асинхронно и всегда возвращает true => find quits в первом файле.

Если мы хотим, чтобы find остановился, когда head закончила, find также должен получить SIGPIPE, который получает grep (сигнал 13). Это означает, что find должен отправить что-то через трубу.

Вот быстрый и грязный хак, дополненный предложениями Стефана:

 find /path/ -type f -exec grep -H -m 1 --line-buffered 'pattern' {} + -printf '\r' | head -n 5 

С -printf '\r' я заставляю find выводить безвредный символ, который (надеюсь) не изменит вывод grep . Когда head остановится, find получит SIGPIPE и остановится.

[update2] Я предупреждал вас, что это грязный хак. Вот лучшее решение:

 find /path/ -type f -exec grep --quiet 'pattern' {} ";" -print | head -n 5 

Здесь больше нет grep который печатает имя файла, но find => не более «grep, завершенным сигналом 13», и find остановки с head . Проблема в том, что согласованные строки больше не печатаются grep .

[update3] Наконец, как предложил @Andrey, бесстыдно отвратительная команда ниже решила бы этот последний вопрос:

 find /path/ -type f \ -exec grep --quiet 'pattern' {} \; \ -printf '%p:' \ -exec grep -h -m 1 'pattern' {} \; \ | head -n 5`