Search code examples
bashgrepfindcygwinexec

Using -exec on output from bashrc function


In Cygwin, I wrote a bashrc function rg, which is basically a recursive grep statement:

rg () {
    find . -type f -exec grep -il $1 {} \;
}

This works well, but now I need to run an additional grep on each output line to check for another word. I basically want it to list each file that contains both words (not necessarily on the same line). I tried this command:

rg Word1 -exec grep -il Word2 {} \;

This seems to just output the same files containing "Word1".

I tried this next command, and I thought it'd just output "Ha" on each line, but it still keeps listing the files from the "rg Word1" statement.

rg Word1 -exec echo "Ha" \;

So I'm clearly doing something wrong here. Can anyone clear my confusion? I'm aware there's a way to do this within grep itself, but that seems to work on a per-line basis. I'm guessing what I'm trying to do is pretty common. Also once I get this working, I'd like to put it in another bashrc function for convenience. Not sure if that's makes it more complicated or not.


Solution

  • If you have it or can get it, the xargs command collects arguments for a command from its standard input, then runs the designated command. You could combine that with your rg function to filter the function's output. For example,

    rg Word1 | xargs grep -il Word2
    

    The arguments xargs reads from its standard input -- the file names emitted by function rg -- will be appended to the given command (grep -il Word2), and the resulting command run. If xargs's input is long enough, the arguments will be split across multiple invocations of the grep command, which makes no difference to the output (in this case), but avoids command execution failing on account of too many arguments.


    Consider also structuring function rg in the same way (i.e. using xargs) to minimize the number of separate grep processes that are executed. Starting a new process is one of the more expensive things you can do.