find -exec + vs find | xargs: which one to choose?
Safely piping file names to xargs
requires that your find
supports the -print0
option and your xargs
has the corresponding option to read it (--null
or -0
). Otherwise, filenames with unprintable characters or backslashes or quotes or whitespace in the name may cause unexpected behavior. On the other hand, find -exec {} +
is in the POSIX find
spec, so it is portable, and it is about as safe as find -print0 | xargs -0
, and definitely safer than find | xargs
. I'd recommend never doing find | xargs
without -print0
.
You might want to chain calls to find (once, when you learned, that it is possible, which might be today). This is, of course, only possible as long as you stay in find. Once you pipe to xargs it's out of scope.
Small example, two files a.lst and b.lst:
cat a.lst
fuddel.sh
fiddel.sh
cat b.lst
fuddel.sh
No trick here - simply the fact that both contain "fuddel" but only one contains "fiddel".
Assume we didn't know that. We search a file which matches 2 conditions:
find -exec grep -q fuddel {} ";" -exec grep -q fiddel {} ";" -ls
192097 4 -rw-r--r-- 1 stefan stefan 20 Jun 27 17:05 ./a.lst
Well, maybe you know the syntax for grep or another program to pass both strings as condition, but that's not the point. Every program which can return true or false, given a file as argument, can be used here - grep was just a popular example.
And note, you may follow find -exec with other find commands, like -ls or -delete or something similar. Note, that delete not only does rm (removes files), but rmdir (removes directories) too.
Such a chain is read as an AND combination of commands, as long as not otherwise specified (namely with an -or
switch (and parens (which need masking))).
So you aren't leaving the find chain, which is a handy thing. I don't see any advantage in using -xargs, since you have to be careful in passing the files, which is something find doesn't need to do - it automatically handles passing each file as a single argument for you.
If you believe you need some masking for finds {} braces, feel free to visit my question which asks for evidence. My assertion is: You don't.
If you use the -exec ... ;
form (remembering to escape the semicolon), you're running the command once per filename. If you use -print0 | xargs -0
, you run multiple commands per filename. You should definitely use the -exec +
form, which puts multiple files in a single command line and is much faster when a large number of files is involved.
A big plus of using xargs
is the ability to run multiple commands in parallel using xargs -P
. On multi-core systems, that can provide huge time savings.