Shell – Correct Location for Piping and Redirecting Output in Find -exec

findfreebsdio-redirectionshell

I'm using find with the -exec option in console, on FreeBSD (for example, find . -exec sha1 {} \;).

  1. How do I correctly place (and if needed, escape) redirect and other execution control symbols that could syntactically apply to either of the exec command or the find command, such as > >> | and &, where I might want the symbol to apply to the find or the exec commands on different occasions?

  2. On the same theme, what is the syntax if I want to use tee to view the output of the -exec commands on the console (to watch progress) and also appended to a file (for later use)?

(I know shells vary but hopefully the most used shells such as sh/csh are quite similar?)

Update:

In part, I want to learn if nested find -exec can be done without scripting, as a simple command. Here is the use-case that prompted the questio :

  • outer find: find all subdirs matching DIR_MATCH_TEXT in DIR1
  • inner find: for each matching subdir found in the outer loop (call it DIR2), execute the command
    find DIR2 -name "FILE_MATCH_TEXT" -exec sha1 {} \; >> DIR1/DIR2_hashes.txt

The aim being to create a set of files, one for each matching subdir, containing the output of some find -exec action on that subdir.

By this I mean that, if /backups contains /jan2017 and /feb2017, the result will be two files at /backups/jan2017_hashes.txt and /backups/feb2017_hashes.txt, with jan2017_hashes.txt containing the output from sha1 for (say) all .pdf files in /backups/jan2017, and feb2017_hashes.txt containing the output from sha1 for all .pdf files in /backups/feb2017.

From replies so far I gather the outer find would have to use a shell call as the argument to -exec?

Best Answer

I/O redirection and piping are functions of shells (e.g., bash). Looking at the source code of the find command (https://www.gnu.org/software/findutils/), the -exec option forks and directly execs the given command, it does not send it though any shell. Given that, without help, you cannot include piping and redirection in the argument to find.

You can, however, have find invoke a shell and have that shell in turn invoke your command. When running the same through a shell, you have access to the I/O redirection operations exposed by the shell. Consider, for example:

find . -type f -exec bash -c 'ls -l "$1" >> /tmp/result' find-sh {} \;

That will run ls -l on each file found by find (the shell refers to that file in this context as $0) and redirect the output to /tmp/result. Note that I used >> instead of >. Here, find is invoking a shell for each file, so > will result in each result overwriting the previous -- you'll end up with a file that contains only the output from the last command.

Pipes will work as well, with the same caveat of "one exec per file".

find . -type f -exec bash -c 'ls -l "$1" | grep something' find-sh {} \;

All that said, it's not immediately clear to me why you'd want to apply redirection within the context of the -exec, or what it would buy you over performing the redirection on the output of find itself.

Related Question