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

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

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

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

  • Обнаружение файлов расширений не выполняется за период
  • Как я могу искать / просматривать в памяти какого-либо процесса?
  • Grep для поиска шаблона в файле
  • Команда Awk против Grep
  • Удалите несколько последовательных имен файлов, но игнорируйте часть первого
  • Сценарий Bash не видит SIGHUP?
  •  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 )?

  • Получение pid скрипта bash от себя
  • Cron только время от времени отправляет электронную почту на выходе и ошибки
  • Как grep все xml-файлы, которые не начинаются с "<"
  • Как узнать все файлы на других машинах, используя сценарий оболочки bash?
  • Завершение скрипта оболочки bash, работающего в фоновом режиме
  • Подавлять сообщения об усечении файла при использовании хвоста
  • 3 Solutions collect form web for “выход ограничения на выход И избежать сигнала 13”

    Поскольку вы уже используете расширения 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` 
    Linux и Unix - лучшая ОС в мире.