How do I grep for a string recursively through all .gz files in all directories and subdirectories?

migrated from stackoverflow.com May 25 '10 at 4:18

This question came from our site for professional and enthusiast programmers.


@Steve Weet is almost there. The use of /dev/null as an additional argument is a nice way to force the filename to be shown (I'll remember that, thanks Steve) but it still runs the exec for every file found -- a huge overhead.

You want to run zgrep as few times as you can, getting the most out of each execution:

find . -iname '*.gz' -print0 | xargs -0 zgrep PATTERN

xargs will supply as many args (filenames) as possible to zgrep, and repeatedly execute it until it has used all the files supplied by the find command. Using the -print0 and -0 options allows it to work if there are spaces in any of the file or directory names.

On Mac OS X, you can achieve the same effect without xargs:

find . -iname '*.gz' -exec zgrep PATTERN {} +
  • +1 That's really nice. I hadn't realised that xargs passed more than one argument. Much of my *nix command line-fu is 20 years old and I don't think xargs did that 20 years ago. – Steve Weet May 24 '10 at 9:14
  • It turns out that find on os/x behaves the same way as xargs – Steve Weet May 24 '10 at 9:20
  • 1
    See my comment to Steve Weet's answer regarding the '+' ending to -exec. – Daniel Andersson Mar 27 '12 at 15:55
  • Use -H to always show the file name with the matching line, in GNU grep at least. – Daniel Andersson May 11 '12 at 7:09
$ zgrep --help
Usage: /bin/zgrep [OPTION]... [-e] PATTERN [FILE]...
Look for instances of PATTERN in the input FILEs, using their
uncompressed contents if they are compressed.

So something like

find . -iname "*.gz" -exec zgrep PATTERN {} \
  • The -exec will spawn a new instance of zgrep for every file it iterates over preventing you from seeing the file name. It would be better to use zgrep -r to go through a tree or if the -r doesn't work, pipe the output of the find through xargs zgrep – Noufal Ibrahim May 21 '10 at 10:28
  • I get /bin/zgrep: -r: option not supported on my newly installed ubuntu system. – aioobe May 21 '10 at 10:31
  • You can use xargs instead then. – Noufal Ibrahim May 24 '10 at 9:24
  • See my comment to Steve Weet's answer regarding the '+' ending to -exec. – Daniel Andersson Mar 27 '12 at 15:55

@aioobe is almost there. The command will do the job but won't tell you the file name

The following should tell you the filename as well:

find . -iname "*.gz" -exec zgrep PATTERN {} /dev/null \;

The addition of /dev/null will ensure that zgrep sees two filenames so it will show you the name of the file if it finds the string


Further research reveals that for my machine (OS/X) the -exec argument to find will add as many filenames as possible (similar to the way xargs behaves).

  • That's pretty cool, I didn't know that about the OSX -exec - I'm all about portability so I wouldn't use it in a script, but great for the command prompt. – Stephen P May 24 '10 at 15:36
  • For other versions of find, using '+' instead of '\;' to end the exec statement will do the same as OSX, by the stories in this thread, does by default. See the manual entry for '-exec command {} +'. It is not true of all versions of find, but most of the modern ones (e.g. in Debian based distros). – Daniel Andersson Mar 27 '12 at 15:54
  • Use -H to always show the file name with the matching line, in GNU grep at least, instead of the /dev/null hack. – Daniel Andersson May 11 '12 at 7:10

The following works a treat in zsh

for archive in **/*.gz; do
    echo "[${archive}] "
    gzip -dc ${archive} | grep -n "String"

It may also work in bash, ksh, etc...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy