Command line wizardry: spaces in file names with find | grep | xargs

5

3

I need a way to run grep on a list of files which may contain spaces in the file name. On a list of files with no spaces, it's pretty straight forward and easy. Also, I know how to deal with spaces in the file names for find and xargs. I'm just looking for a way to use grep with the -print0 command.

I'm looking for text inside the file, not in the filename...

This lists all files:

find * -print 0 | xargs -0 -i{} echo {}

but say I have to do a grep (or some other command) inbetween.

This lists files which contain "howdy doody" inside the file:

find * | xargs grep -l "howdy doody" | xargs -i{} echo {}

This doesn't work, grep doesn't know how to recognize null terminated lines. Did I miss something in grep's man page?

find * -print0 | xargs grep -l "howdy doody" | xargs -0 -i{} echo {}

Roy Rico

Posted 2009-12-07T20:28:58.620

Reputation: 4 808

Answers

15

Are you looking for "howdy doody" in the filename, or within the file?

# find files with "howdy doody" in the filename
find * -name "*howdy doody*" -print0 | xargs -0 ...

xargs is what you need to use to split the null-terminated output from find -print0. In your example, the echo is extraneous; don't bother with it.

# find files containing "howdy doody"
find * -print0 | xargs -0 grep -l "howdy doody"

# find files containing "howdy doody" and do further processing

# multiple xargs version
find * -print0 | xargs -0 grep -l "howdy doody" | xargs -i{} do-something "{}"

# "sh -c" version
find * -print0 | xargs -0 -i{} sh -c 'grep -l "howdy doody" "{}" && do-something "{}"'

# notes: 
#   "sh -c" allows us to run a complex command with a single xargs
#   "{}" handles spaces-in-filename
#   handles any &&, ||, | command linking

You can also run your command directly from the find with -exec. The difference is that find -exec runs the command once per file found; xargs adds filenames to the end of the command (up to system limit), so it runs the command fewer times. You can simulate this with xargs -n1 to force xargs to only run one command per input.

# grep once per file
find * -exec grep -l "howdy doody" {} \; | ...

quack quixote

Posted 2009-12-07T20:28:58.620

Reputation: 37 382

depending on what your "further processing" is, the multiple-xargs version may be more (or less) preferable than the "sh -c" version. – quack quixote – 2009-12-07T21:37:46.580

the echo in my example is just to debug/clarify the question. I like this approach, tho yet somewhat verbose. Thanks – Roy Rico – 2009-12-08T02:48:17.280

gotcha. i thought maybe it was a placeholder for additional commands (useful when you're trying to figure out syntax for something). – quack quixote – 2009-12-08T02:56:59.353

3

You can do it all in one command, using the -exec argument to find:

find . -name "*howdy doody*" -exec echo I found file {} hooray \;

The escaped semicolon at the end of the command to execute is required by find.

Or, if you're looking in the contents of files:

grep -R "howdy doody" *

This will show all text matches, add -l to the grep to just get a list of files

davr

Posted 2009-12-07T20:28:58.620

Reputation: 4 809

I'm trying to find :howdy doody" inside the file, not in the name. but it's good to know that the -exec option uses {} for the var, i didn't know that. – Roy Rico – 2009-12-08T02:42:10.627

Oh ok, your example commands show using grep to find "howdy doody" in the file names. See my second example. The other guy's answers should work too, but simpler commands are easier to remember, and should in theory be quicker if it's just a single command instead of multiple. – davr – 2009-12-08T02:47:21.803

yea, sorry that's an error on my part, i forgot the extra xargs in the command. I updated my question to reflect that. sorry! – Roy Rico – 2009-12-08T02:50:15.637

I don't like grep -R option as it doesn't have enough pipes in it. I like my unix commands how i like my head shops, with a lot of pipes in 'em! haha seriously tho, thanks, i learned something else – Roy Rico – 2009-12-08T02:56:06.797

Also, keeping davr as answered because it works generically with other commands, not just grep. You're answer is better (shorter) for this specific example tho. – Roy Rico – 2009-12-08T02:59:20.100