Вопрос или проблема
У меня есть куча заархивированных лог-файлов, и я хочу искать во всех них строку. Я попробовал это, но это не работает:
find ./ -name "*.log.zip" -exec gzip -dc {} | grep ERROR \;
Это дает мне:
find: неполное выражение
grep: не удается открыть ;
Что я хочу, так это чтобы для каждого файла .log.zip его разархивировали и выдали в выводе строки с “ERROR”. Я делаю это на AIX, на всякий случай.
В вашем синтаксисе есть ошибка. Команда find ожидает \;
или \+
, но читает |
. Grep пытается открыть файл с именем “;”. Разница между окончанием -exec с помощью точки с запятой и плюса заключается в том, что команда выполняется один раз для всех файлов (+) и выполняется один раз для каждого файла (;).
Попробуйте это:
find ./ -name "*.log.zip" -exec zcat {} \+ | grep ERROR
# или
find ./ -name "*.log.zip" -exec sh -c 'zcat {} | grep ERROR' \;
Если вам не нужно знать, какие заархивированные лог-файлы содержат строку:
find ./ -name "*.log.zip" -type f -exec gzip -dc {} + | grep ERROR
Если вам нужно знать, какие файлы содержат строку:
find ./ -name "*.log.zip" -type f -exec sh -c 'gzip -dc -- "$1" | grep -q ERROR' findsh {} \; -print
Первая команда находит файлы и передает эти имена файлов в опцию -exec
. Я добавил ограничение -type f
к команде, чтобы убедиться, что мы совпадаем только с файлами — представьте, что кто-то запустил “mkdir foo.log.zip”. gzip
разархивирует каждый файл в stdout; мы игнорируем любые ошибки find
или gzip
с помощью 2>/dev/null
; весь stdout этой команды затем передается через grep
. Синтаксис +
в конце -exec
позволит передать столько имен файлов, сколько поместится, что минимизирует количество вызовов gzip
. Так как gzip
отправляет все содержимое файла в stdout, grep
теперь просто получает поток байтов — без имен файлов — и будет печатать любые подходящие строки.
С другой стороны, если вам нужно знать совпадающие имена файлов, вам нужно захватить это раньше в конвейере.
На системе GNU/Linux (которая имеет zgrep
), вы можете сделать это непосредственно:
find . -name "*.log.zip" -type f -exec zgrep -l ERROR {} +
Это передаст (сколько поместится) имена файлов в zgrep
, которому мы затем указываем напечатать совпадающие имена файлов (`-l` опция).
На системе AIX вы можете воспроизвести эту функциональность с помощью небольшого скрипта оболочки. Синтаксис может выглядеть немного устрашающе, но давайте разберем его снаружи внутрь:
find ... -exec sh -c ' ... ' findsh {} \; -print
Вышеупомянутое выражение собирает один совпадающий файл за раз (\;
) и отправляет его в качестве аргумента в данный скрипт sh
; если скрипт возвращает успех, имя файла печатается, в противном случае нет. Часть findsh
является произвольным текстом; это становится аргументом $0
для sh
, давая встроенному скрипту оболочки имя.
Примечание:
Синтаксис
{}
должен быть снаружи скрипта оболочки; в противном случае это может привести к произвольному выполнению команд. На AIX фигурные скобки не заменяются внутри параметра-exec
, так что вы бы увидели ошибки “gzip: {}.gz: Нет такого файла или каталога”, если бы вы попробовали это. На GNU/Linuxfind
действительно заменяет имя файла внутри скрипта оболочки, что означает, что если кто-то создаст файл с именем$(touch foo).log.zip
, вы в итоге получите файл с именем “foo”, потому что скрипт оболочки инициирует другой уровень разбора для имен файлов. См. больше по этому поводу в этом вопросе UNIX и Linux: Можно ли использоватьfind -exec sh -c
безопасно?
Как только имена файлов были переданы по одному, скрипт оболочки выглядит следующим образом:
gzip -dc -- "$1" | grep -q ERROR
Имя файла находится в $1
, так что мы вызываем gzip -dc
на нем. По привычке, я стараюсь пометить конец опций перед произвольным именем файла, на случай, если это имя файла начинается с дефиса — или любого другого символа — который может быть неверно интерпретирован командой как опция. Поскольку наша команда find
специально начинает поиск с ./
, все совпадающие имена файлов будут начинаться с этой строки, так что они никогда не будут выглядеть как опции для gzip, но лучше иметь безопасные привычки. После того как gzip передал содержимое, grep тихо ищет строку. Если grep находит строку, оболочка успешно завершится, позволив последующей печати; в противном случае это вызовет -exec
, чтобы вернуть ложный/ошибочный код завершения, так что имя файла не будет напечатано.
Ответ или решение
Поиск ошибок в сжатых логах с помощью find
, gzip
и grep
Если у вас есть несколько сжатых лог-файлов (например, с расширением .log.zip
), и вы хотите выполнить поиск по содержимому этих файлов на наличие определенной строки — например, "ERROR"
— вам потребуется использовать правильный синтаксис команд find
, gzip
и grep
. Рассмотрим, как это сделать, чтобы избежать распространенных ошибок.
Проблема с вашим первоначальным запросом
Ваш первоначальный пример команды:
find ./ -name "*.log.zip" -exec gzip -dc {} | grep ERROR \;
возвращает сообщение об ошибке. Причина заключается в том, что find
ожидает завершение команды -exec
синтаксисом \;
или +
, а использование |
нарушает это ожидание. В результате grep
ошибочно интерпретирует ;
как имя файла.
Корректная реализация
- Поиск строк без указания имени файла:
Если вам не важно, в каких файлах содержится строка, вы можете использовать следующую команду:
find ./ -name "*.log.zip" -type f -exec gzip -dc {} + | grep ERROR
Здесь -type f
гарантирует, что будут обрабатываться только файлы. Символ +
в конце -exec
позволяет передать группе файлов команду gzip
, что снижает количество вызовов этой команды и делает процесс более эффективным.
- Поиск строк с указанием имени файла:
Если необходимо знать, в каких именно файлах найдена искомая строка, используйте следующую команду:
find ./ -name "*.log.zip" -type f -exec sh -c 'gzip -dc -- "$1" | grep -q ERROR' findsh {} \; -print
В этом случае используется оболочка sh
, чтобы обработать каждый файл по отдельности. При этом переменная $1
содержит имя текущего файла, а -q
в grep
указывает на тихий режим: команда не выводит строки на экран, а только возвращает код завершения. Если строка найдена, файл будет напечатан.
Использование zgrep
на Linux
На системах GNU/Linux можно напрямую использовать zgrep
для этой задачи:
find . -name "*.log.zip" -type f -exec zgrep -l ERROR {} +
Опция -l
указывает на то, что следует выводить только имена файлов, содержащих искомую строку. Этот подход гораздо проще и эффективнее, чем комбинировать несколько команд.
Заключение
Использование команд find
, gzip
и grep
для обработки сжатых логов требует внимательности к синтаксису и логике выполнения команд. Применяя описанные стратегии, вы сможете эффективно находить необходимые строки в сжатых файлах, минимизируя ошибки и улучшая скорость выполнения задач. Тщательный подход к написанию скриптов и команд позволит значительно повысить скорость работы с логами и облегчит администрирование систем.