Shell – pipeline as find’s -exec argument

command linefindpipeshell

Is it possible to use a pipeline command as an argument to find's -exec option? This means, I want to do something like this:

find . -name CMakeLists* -exec cat '{}' | grep lib \;

where I am trying to execute cat '{}' | grep lib for each file,
but this doesn't work. Neither does quoting work. Does anyone have any advice?

Update:

The particular question was answered. Now, is there a way for a generic find <path> -type f -name <name> -exec <pipeline-command> pattern to work?

Best Answer

find . -type f -name "CMakeLists*" -exec grep lib /dev/null {} +

This finds files in the current directory whose basename begins or is the string CMakeLists. The argument is escaped (double quoted) so that the shell doesn't expand it before find runs.

There is no need to add cat with a pipe to grep --- it's a useless process with useless IO, here. Adding /dev/null insures that grep will report the filename along with the matching line(s) when there is more than one file to match.

By using {} + as the terminating sequence to the -exec argument, multiple filenames are passed to each invocation of the grep command. Had we used {} \; then a grep process would have been spawned for every file found. Needless process instantiation is expensive if done hundreds or thousands of times.

To use a pipe with a find -exec argument, you need to invoke the shell. A contrived example could be to grep for the string "one" but only if the string "two" isn't present too. This could be done as:

find . -type f -name "CMakeLists*" -exec sh -c 'grep one "$@"|grep -v two' sh {} +

This is predicated on the comments, below, by @muru, @Serg and @Scott, with thanks.

Related Question