find: Multiple Files
3.3.2 Multiple Files
--------------------
Sometimes you need to process files one at a time. But usually this is
not necessary, and, it is faster to run a command on as many files as
possible at a time, rather than once per file. Doing this saves on the
time it takes to start up the command each time.
The '-execdir' and '-exec' actions have variants that build command
lines containing as many matched files as possible.
-- Action: -execdir command {} +
This works as for '-execdir command ;', except that the result is
always true, and the '{}' at the end of the command is expanded to
a list of names of matching files. This expansion is done in such
a way as to avoid exceeding the maximum command line length
available on the system. Only one '{}' is allowed within the
command, and it must appear at the end, immediately before the '+'.
A '+' appearing in any position other than immediately after '{}'
is not considered to be special (that is, it does not terminate the
command).
-- Action: -exec command {} +
This insecure variant of the '-execdir' action is specified by
POSIX. The main difference is that the command is executed in the
directory from which 'find' was invoked, meaning that '{}' is
expanded to a relative path starting with the name of one of the
starting directories, rather than just the basename of the matched
file. The result is always true.
Before 'find' exits, any partially-built command lines are executed.
This happens even if the exit was caused by the '-quit' action.
However, some types of error (for example not being able to invoke
'stat()' on the current directory) can cause an immediate fatal exit.
In this situation, any partially-built command lines will not be invoked
(this prevents possible infinite loops).
At first sight, it looks like the list of filenames to be processed
can only be at the end of the command line, and that this might be a
problem for some commands ('cp' and 'rsync' for example).
However, there is a slightly obscure but powerful workaround for this
problem which takes advantage of the behaviour of 'sh -c':
find startpoint -tests ... -exec sh -c 'scp "$@" remote:/dest' sh {} +
In the example above, the filenames we want to work on need to occur
on the 'scp' command line before the name of the destination. We use
the shell to invoke the command 'scp "$@" remote:/dest' and the shell
expands '"$@"' to the list of filenames we want to process.
Another, but less secure, way to run a command on more than one file
at once, is to use the 'xargs' command, which is invoked like this:
xargs [OPTION...] [COMMAND [INITIAL-ARGUMENTS]]
'xargs' normally reads arguments from the standard input. These
arguments are delimited by blanks (which can be protected with double or
single quotes or a backslash) or newlines. It executes the COMMAND (the
default is 'echo') one or more times with any INITIAL-ARGUMENTS followed
by arguments read from standard input. Blank lines on the standard
input are ignored. If the '-L' option is in use, trailing blanks
indicate that 'xargs' should consider the following line to be part of
this one.
Instead of blank-delimited names, it is safer to use 'find -print0'
or 'find -fprint0' and process the output by giving the '-0' or '--null'
option to GNU 'xargs', GNU 'tar', GNU 'cpio', or 'perl'. The 'locate'
command also has a '-0' or '--null' option which does the same thing.
You can use shell command substitution (backquotes) to process a list
of arguments, like this:
grep -l sprintf `find $HOME -name '*.c' -print`
However, that method produces an error if the length of the '.c' file
names exceeds the operating system's command line length limit. 'xargs'
avoids that problem by running the command as many times as necessary
without exceeding the limit:
find $HOME -name '*.c' -print | xargs grep -l sprintf
However, if the command needs to have its standard input be a
terminal ('less', for example), you have to use the shell command
substitution method or use either the '--arg-file' option or the
'--open-tty' option of 'xargs'.
The 'xargs' command will process all its input, building command
lines and executing them, unless one of the commands exits with a status
of 255 (this will cause xargs to issue an error message and stop) or it
reads a line contains the end of file string specified with the '--eof'
option.
Menu