Как я рекурсивно grep через файлы .gz?

135

Я использую скрипт для регулярной загрузки моих сообщений gmail, которые сжимают необработанные .eml в файлы .gz. Сценарий создает папку для каждого дня, а затем сжимает каждое сообщение в свой собственный файл.

Я хотел бы найти в этом архиве «строку».

Один Grep, похоже, не делает этого. Я также попробовал SearchMonkey.

Kendor
источник
16
использовать zgrep:zgrep - search possibly compressed files for a regular expression
Аркадиуш Драбчик

Ответы:

142

Если вы хотите рекурсивно выполнять grep во всех файлах .eml.gz в текущем каталоге, вы можете использовать:

find . -name \*.eml.gz -print0 | xargs -0 zgrep "STRING"

Вы должны убежать от первого, *чтобы оболочка не интерпретировала его. -print0говорит find для печати нулевого символа после каждого найденного файла; xargs -0читает из стандартного ввода и запускает команду после него для каждого файла; zgrepработает как grep, но сначала распаковывает файл.

Дж. К. Стаффорд
источник
3
«-print0» и «-0» не являются обязательными. xargs по умолчанию использует '\ n'.
Хайме М.
1
Они необходимы, если в путях могут быть пробелы; нет никакой другой причины, кроме сложности, чтобы не использовать их.
Даниэль Гриском
2
zgrepна самом деле кажется быстрее, чем grepработать с несжатыми файлами. Это должно быть потому, что сжатые файлы могут быть прочитаны с жесткого диска и распакованы быстрее, чем чтение несжатого файла с жесткого диска.
Геремия
@JaimeM. по умолчанию xargsиспользует пробелы (пробелы). Конечно, в файлах почти никогда не бывает символов новой строки, но пробелы не являются неслыханными (даже если большинство типов UNIXy недовольны ими). Тем не менее, вы можете упростить, не беспокоясь о пробелах, еще проще: find . -name '*.eml.gz' -exec zgrep "STRING" {} +это получает столько же аргументов за запуск xargs, безопасность -print0/ -0и все без дополнительных затрат на запуск и процессирование процесса, и довольно лаконично. -execwith +- это POSIX, поэтому, насколько мне известно, это должно быть в большинстве последних UNIX-подобных систем.
ShadowRanger
@Jared Есть ли способ сделать поиск по шаблону, зная только начало шаблона файла? Например, у меня есть файлы .gz с метками даты / времени в конце. ABCLog04_18_18_2_21.gz Есть ли способ рекурсивного поиска файлов, начинающихся с ABC *. Я попытался заменить \*.eml.gzв вашем примере выше на ABCLog*и получить ошибку о формате файла .:find: paths must precede expression: ABCLog-2018-03-12-10-16-1.log.gz Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
DevelopingDeveloper
68

Здесь много путаницы, потому что ее нет zgrep. У меня есть две версии в моей системе, zgrepиз gzipи zgrepиз zutils. Первый - это просто скрипт-обертка, который вызывает gzip -cdfq. Он не поддерживает -r, --recursiveпереключатель. 1
Последняя представляет собой c++программу , и она поддерживает в -r, --recursiveопции.
Запуск zgrep --version | head -n 1покажет, какой из них (если есть) является значением по умолчанию:

zgrep (gzip) 1.6

это скрипт-обертка,

zgrep (zutils) 1.3

это cppисполняемый файл.
Если у вас есть последний, вы можете запустить:

zgrep 'pattern' -r --format=gz /path/to/dir

В любом случае, как и предполагалось, find+ zgrepбудет одинаково хорошо работать с любой версией zgrep:

find /path/to/dir -name '*.gz' -exec zgrep -- 'pattern' {} +

Если zgrepотсутствует в вашей системе (очень маловероятно), вы можете попробовать с:

find /path/to/dir -name '*.gz' -exec sh -c 'gzip -cd "$0" | grep -- "pattern"' {} \;

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


1: потому что это было бы проблематично

don_crissti
источник
1
если zgrepzutils недоступен, вы можете установить его в Ubuntu с помощью sudo apt-get install zutils.
therealmarv
1
Продолжение с @therealmarv ... и тогда Ubuntu будет использовать zutils zgrep вместо gzip. Тогда -r работает!
Элайджа Линн
Есть ли способ напечатать номер строки файла, с которым сопоставляется шаблон?
DogEatDog
@DogEatDog - так же , как grep -n, zgrep -nнапечатает no.s. линии Это в руководстве ...
don_crissti
7

agэто вариант grep, с некоторыми приятными дополнительными функциями.

  • имеет опцию -z для сжатых файлов,
  • имеет много особенностей.
  • это быстро

Так:

ag -r -z your-pattern-goes-here   folder

Если не установлен,

apt-get install silversearcher-ag   (debian and friends)
yum install the_silver_searcher     (fedora)
brew install the_silver_searcher    (mac)
JJoao
источник
1
Я получаю ag: truncated file: Successв результате. Любой другой флаг я должен добавить?
Яр
4

Одна рекурсия легка:

   -r, --recursive
          Read all files  under  each  directory,  recursively,  following
          symbolic  links  only  if they are on the command line.  This is
          equivalent to the -d recurse option.

   -R, --dereference-recursive
          Read all files under each directory,  recursively.   Follow  all
          symbolic links, unlike -r.

Однако для сжатых файлов вам нужно что-то вроде:

shopt globstar 
for file in /path/to/directory/**/*gz; do zcat ""$file" | grep pattern; done

path/to/directory должен быть родительским каталогом, который содержит подкаталоги для каждого дня.


zgrepэто очевидный ответ, но, к сожалению, он не поддерживает -rфлаг. От man zgrep:

Эти параметры grep приведут к завершению работы zgrep с кодом ошибки: (- [d rR zZ] | --di * | --exc * | --inc * | --rec * | --nu *).

Тердон
источник
3

Если ваша система имеет zgrep, вы можете просто

zgrep -irs your-pattern-goes-here the-folder-to-search-goes-here/

Если в вашей системе нет zgrep, вы можете использовать команду find для запуска zcat и grep для каждого файла следующим образом:

find the-folder-to-search-goes-here/ -name '*.gz' \ -exec sh -c 'echo "Searching {}" ; zcat "{}" | grep your-pattern-goes-here ' \;

Нейт из Каламазу
источник
Извините меня за это ... файлы, которые нужно найти, имеют глубину пару слоев. ~ / gmvault-db / db / 2015-02 содержит папку для каждого архивного месяца, а затем под ней хранятся файлы .gz для этого месяца. Если я буду искать .mil внутри всего этого дерева, это то, что я буду делать? find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Searching {}"; zcat "{}" | grep .mil '\;
Кендор
1
Это нормально - "r" в -irs заставит zgrep выполнять рекурсивный поиск. По умолчанию команда find работает рекурсивно, поэтому любой файл, заканчивающийся на .gz, будет zcatted и передан в grep. (и {} будет расширен до относительного пути к файлу, который должен быть найден). Поэтому, когда вы получите удар, ему будет предшествовать Searching ~/gmvault-db/db/2015-02/03/whatever.gz
Нейт из Каламазу
Вот что я получаю: find: "пути должны предшествовать выражению: -exec" Вот команда, которую я использовал: find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Поиск { } "; zcat "{}" | grep .mil '\;
Кендор
уберите обратную косую черту между '* .gz' и -exec.
Нейт из Каламазу
4
zgrepне возьму -rфлаг по какой-то причине. Это упоминание в man zgrep(также см. Мой ответ).
Terdon
0

xzgrep -l "строка" ./*/*.eml.gz

xzgrep является производной от утилит zgrep (less / bin / xzgrep)

Со страницы Man:

xzgrep вызывает grep (1) для файлов, которые могут быть распакованы или сжаты с помощью xz (1), lzma (1), gzip (1), bzip2 (1) или lzop (1). Все указанные параметры передаются непосредственно в grep (1).

-l распечатать соответствующее имя файла

-R для рекурсии не будет работать, так как это специально запрещено в скрипте, однако простое перетаскивание оболочки должно привести нас туда

./*/*.eml.gz

из относительного пути, где ./today/sample.eml.gz, совпадают во всех экземплярах, которые находятся на один уровень ниже нашего относительного положения в оболочке, что заканчивается на «.eml.gz»

Джон
источник