For the better part
of my career I used 'find' incorrectly, or at the very least very
naively. The '-exec' trickery eluded me for over a decade;
I often relied on running child processes or _worse_ saving results to a file to run subsequent command on.
For instance, if I
wanted to examine log files that had 'error' in it, I'd fashion a
command to open VI with the arguments of the result from a gr
ep command;
e.g. $ vi `grep -li error *.log`
The command line
would first execute the grep command, the results formed the arguments
to the vi command. Alternatively, I'd very occasionally
use xargs as an
alternative. Alternatively, to the alternative, I'd run the equivalent
of the grep command and redirect the output to a file, then 'cat' the file contents as arguments to whatever command I wanted.
e.g.
$ grep -li error *.log > files.txt
$ vi `cat files.txt`
Clumsy, clumsier and
often times clumsiest. But, lacking an understanding of the '-exec'
option, it was what I knew, got me where I needed to be, but was a source of embarrasement for someone who used Linux professionally for years.
Then, about 8 years
ago, I spent some time familiarizing myself with '-exec' and everyday
tasks got a bit easier. These past days I've been spending time on chaining '-exec' commands, something I wasn't familar with until recently.
Let's say you are
asked to search a server for scripts that use 'ftp', not 'sftp', as a
security audit. A whole lotta scripts exist that use neither, a subset of
them using ftp/sftp, and a subset of that subset exclusively use ftp
(our desired file list). An approach would be to first find all scripts that reference 'ftp', then filter out the ones that use 'sftp'.
$ find /src/scripts/ -type f -exec grep -l 'sftp' {} \; #-- finds scripts referencing sftp
$ grep -L 'sftp' [filelist]; #--lists files w/o sftp expression
You want to feed the
results of the first command as arguments to the second command. A
clumsy approach (the one I used for years) would be:
$ grep -L 'sftp' `find /src/scripts/ -type f -exec grep -l 'ftp' {} \;`
But, the equivalent can be accomplished by chaining '-exec' commands in a single command.
$ find /src/scripts/ -type f -exec grep -l 'ftp' {} \; -exec grep -L sftp {} \;
Chained exec
commands are executed left-to-right, each executed as child commands.
Subsequent '-exec' commands are only executed on files/lines that satisfy the
previous '-exec' command. So, 'grep -L sftp...' is only run on the
results of the previous command (e.g. "grep -l 'ftp'...). The end results is first
finding all files containing 'ftp', then finding files that don't
contain 'sftp'. The output of both '-exec' commands, each on seperate lines,
which can be confusing. We can suppress the output from the first
command by specifying '-quiet (or -q)' to the first grep command, as follows:
$ find /src/scripts/ -type f -exec grep -lq 'ftp' {} \; -exec grep -L sftp {} \;
Now, the resulting
file list from the first exec is still passed to the subsequent exec and
the output to stdout is representative of only the second command.