Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I'm running Mac 10.7.5. In a terminal, I'll use this command for finding files with strings

find src/main -name "*" | xargs grep -i 'mystring'

However when the files contain spaces, I'll get results like

grep: images/mystuff/essential-questions-selected-arrow-ela: No such file or directory
grep: 5.24.38: No such file or directory
grep: PM.png: No such file or directory

The actual file in the above example is "essential-questions-selected-arrow-ela 5.24.38 PM.png" How do I run the above command successfully even if the files being searched contain spaces?

share|improve this question

2 Answers 2

up vote 4 down vote accepted

If you want to use find output in xargs, the recommended way is to pipe it using NUL characters to delimit each filename:

find src/main -name "*" -print0 | xargs -0 grep -i 'mystring'

The reason for this is that the shell (and xargs specifically) generally splits arguments (or input in the case of xargs) based on whitespace, i.e. spaces, tabs, and newlines. Once you use -0, xargs will read each field separated by a NUL, which is what find -print0 outputs.

This will work in GNU find and xargs as well as the versions included in OS X. Other versions might not have the options, as they are not required in POSIX.


But then again, this isn't really necessary. The "*" name pattern expands to all possible names. grep can recurse on its own, so all that's needed is:

grep -ri 'mystring' src/main

In Bash 4 (not shipped with OS X by default) you could also do recursive globbing, e.g. over all .txt files, using the globstar option:

shopt -s globstar
grep -i 'mystring' **/*.txt
share|improve this answer
    
good advice. I don't think MacOS uses GNU tools. @daveA, check your man page for find and xargs for options that let you separate the results with a non-whitespace character. –  glenn jackman Dec 19 '13 at 21:25
1  
The BSD variants of find and xargs included in OS X support the -print0 and -0 options, respectively. –  slhck Dec 19 '13 at 21:27

By far the safest and easiest way to do this kind of thing is to use find's -exec option. From man find:

-exec utility [argument ...] ;
     True if the program named utility returns a zero value as its exit status.
     Optional arguments may be passed to the utility.  The expression must be 
     terminated by a semicolon (``;'').  If you invoke find from a shell you 
     may need to quote the semicolon if the shell would otherwise treat it as a 
     control operator.  If the string ``{}'' appears anywhere in the utility 
     name or the arguments it is replaced by the pathname of the current file.  
     Utility will be executed from the directory from which find was executed.  
     Utility and arguments are not subject to the further expansion of shell 
     patterns and constructs.

-exec utility [argument ...] {} +
     Same as -exec, except that ``{}'' is replaced with as many pathnames as 
     possible for each invo-cation invocationcation of utility. This behaviour 
     is similar to that of xargs(1).

In other words the -exec option will run whatever you give it on the results of find, replacing {} with each file (or directory) found. So, to grep for a particular string, you would do:

find src/main -name "*" -exec grep -i 'mystring' {} +

That, however, will also find directories and will give an error. It will work, mind you, it will just complain when you try to run it on a directory, you would have had the same problem using xargs. What you are actually trying to do here is to find all files and only files. In that case, the -name '*' is not needed since find src/main is exactly the same as find src/main -name "*". So, instead of using that, specify that you only want to find files:

find src/main -type f -exec grep -i 'mystring' {} +
share|improve this answer
    
You need to escape or single-quote ; or +. Otherwise they'd be consumed by the shell, not find. –  slhck Dec 19 '13 at 22:00
    
@slhck at least on bash and Linux, there is no reason to escape +, it has no special meaning to the shell, which is one of the reasons I used that instead of ;. –  terdon Dec 19 '13 at 22:03

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.