Search code examples
bashgreppipexargs

Quoting filenames in multi-step xargs bash pipeline


I am creating a bash pipeline where I want to find files that contain specific text and then do something with only those files. I can successfully get the list of files using the find/xargs pairing:

$ find .  -type f -print0 | xargs -0 grep -l mysearchtext
./dir1/my file.txt
./thefile.pdf

Now that I have the files that match, I want to perform another operation on those files only, something like this:

$ find .  -type f -print0 | xargs -0 grep -l mysearchtext | xargs open

This will fail, as the second xargs call interprets ./dir1/my file.txt as two files.

Any suggestions on how to accomplish this last part of the pipeline?


Solution

  • As long as your version of grep supports the --null option (which makes it use the null character as a delimiter after filenames, very like find ... -print0), you can use that:

    find . -type f -print0 | xargs -0 grep -l --null mysearchtext | xargs -0 open
    

    As jhnc points out in a comment, it's also possible to use grep -q as a test with find's -exec primary (and you can also replace the -print0 | xargs -0 with -exec ... +):

    find . -type f -exec grep -q mysearchtext {} \; -print0  | xargs -0 open
    

    Since that's all within the find test expression, you can also replace the -print0 | xargs -0 with -exec ... +:

    find . -type f -exec grep -q mysearchtext {} \; -exec open {} +
    

    Note the difference between -exec ... \;, which runs the command separately for each file and can be used as a test, and -exec ... + which runs the command on batches of files (like xargs), and therefore cannot be used to test individual files.