How do I use a pipe in the exec parameter for a find command?

BashShellFind

Bash Problem Overview


I'm trying to construct a find command to process a bunch of files in a directory using two different executables. Unfortunately, -exec on find doesn't allow to use pipe or even \| because the shell interprets that character first.

Here is specifically what I'm trying to do (which doesn't work because pipe ends the find command):

find /path/to/jpgs -type f -exec jhead -v {} | grep 123 \; -print

Bash Solutions


Solution 1 - Bash

Try this

find /path/to/jpgs -type f -exec sh -c 'jhead -v {} | grep 123' \; -print

Alternatively you could try to embed your exec statement inside a sh script and then do:

find -exec some_script {} \;

Solution 2 - Bash

A slightly different approach would be to use xargs:

find /path/to/jpgs -type f -print0 | xargs -0 jhead -v | grep 123

which I always found a bit easier to understand and to adapt (the -print0 and -0 arguments are necessary to cope with filenames containing blanks)

This might (not tested) be more effective than using -exec because it will pipe the list of files to xargs and xargs makes sure that the jhead commandline does not get too long.

Solution 3 - Bash

With -exec you can only run a single executable with some arguments, not arbitrary shell commands. To circumvent this, you can use sh -c '<shell command>'.

Do note that the use of -exec is quite inefficient. For each file that is found, the command has to be executed again. It would be more efficient if you can avoid this. (For example, by moving the grep outside the -exec or piping the results of find to xargs as suggested by Palmin.)

Solution 4 - Bash

Using find command for this type of a task is maybe not the best alternative. I use the following command frequently to find files that contain the requested information:

for i in dist/*.jar; do echo ">> $i"; jar -tf "$i" | grep BeanException; done

Solution 5 - Bash

As this outputs a list would you not :

find /path/to/jpgs -type f -exec jhead -v {} \; | grep 123

or

find /path/to/jpgs -type f -print -exec jhead -v {} \; | grep 123

Put your grep on the results of the find -exec.

Solution 6 - Bash

There is kind of another way you can do it but it is also pretty ghetto.

Using the shell option extquote you can do something similar to this in order to make find exec stuff and then pipe it to sh.

root@ifrit findtest # find -type f -exec echo ls $"|" cat \;|sh
filename


root@ifrit findtest # find -type f -exec echo ls $"|" cat $"|" xargs cat\;|sh
h

I just figured I'd add that because at least the way i visualized it, it was closer to the OP's original question of using pipes within exec.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhoyhoyView Question on Stackoverflow
Solution 1 - BashMartin MarconciniView Answer on Stackoverflow
Solution 2 - BashPalminView Answer on Stackoverflow
Solution 3 - BashmweerdenView Answer on Stackoverflow
Solution 4 - BashDimitarView Answer on Stackoverflow
Solution 5 - BashXetiusView Answer on Stackoverflow
Solution 6 - BashlinuxgeekView Answer on Stackoverflow