find -exec cmd {} + vs | xargs
Speed difference will be insignificant.
But you have to make sure that:
Your script will not assume that no
file will have space, tab, etc in
file name; the first version is
safe, the second is not.Your script will not treat a file starting with "
-
" as an option.
So your code should look like this:
find . -exec cmd -option1 -option2 -- {} +
or
find . -print0 | xargs -0 cmd -option1 -option2 --
The first version is shorter and easier to write as you can ignore 1, but
the second version is more portable and safe, as "-exec cmd {} +
" is a relatively new option in GNU findutils (since 2005, lots of running systems will not have it yet) and it was buggy recently. Also lots of people do not know this "-exec cmd {} +
", as you can see from other answers.
Which is faster, 'find -exec' or 'find | xargs -0'?
I expect the xargs version to be slightly faster as you aren't spawning a process for each filename. But, I would be surprised if there was actually much difference in practice. If you're worried about the long list xargs sends to each invocation of rm, you can use -l with xargs to limit the number of tokens it will use. However, xargs knows the longest cmdline length and won't go beyond that.
Using semicolon (;) vs plus (+) with exec in find
This might be best illustrated with an example. Let's say that find
turns up these files:
file1
file2
file3
Using -exec
with a semicolon (find . -exec ls '{}' \;
), will execute
ls file1
ls file2
ls file3
But if you use a plus sign instead (find . -exec ls '{}' \+
), as many filenames as possible are passed as arguments to a single command:
ls file1 file2 file3
The number of filenames is only limited by the system's maximum command line length. If the command exceeds this length, the command will be called multiple times.
Solution for find -exec if single and double quotes already in use
In a double quoted string you can use backslashes to escape other double quotes, e.g.
find ... "rm \"\$(...)\""
If that is too convoluted use variables:
cmd='$(...)'
find ... "rm $cmd"
However, I think your find -exec
has more problems than that.
- Using
{}
inside the command string"cd '{}' ..."
is risky. If there is a'
inside the file name things will break and might execcute unexpected commands. $()
will be expanded by bash beforefind
even runs. Sols -t *.pdf | tail -2
will only be executed once in the top directory.
instead of once for each found directory.rm
will (try to) delete the same file for each found directory.rm "$(ls -t *.pdf | tail -2)"
will not work ifls
lists more than one file. Because of the quotes both files would be listed in one argument. Therefore,rm
would try to delete one file with the namefirst.pdf\nsecond.pdf
.
I'd suggest
cmd='cd "$1" && ls -t *.pdf | tail -n2 | sed "s/./\\\\&/g" | xargs rm'
find . -type d -name bak -exec bash -c "$cmd" -- {} \;
find -exec - suppress errors only for find, but not for executed command
Using -exec
option in find
command is integration of xargs
command into find
command.
You can alwayes separate find
from -exec
by piping find
output into xargs
command.
For example:
find / -type f -name "*.yaml" -print0 2>/dev/null | xargs ls -l
Execute bash function from find command
You could manually loop over find
's results.
while IFS= read -rd $'\0' file; do
remodup "$file"
done < <(find "$dir" -name "*.mod" -type f -print0)
-print0
and -d $'\0'
use NUL as the delimiter, allowing for newlines in the file names. IFS=
ensures spaces as the beginning of file names aren't stripped. -r
disables backslash escapes. The sum total of all of these options is to allow as many special characters as possible in file names without mangling.
How do I use a pipe in the exec parameter for a find command?
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 {} \;
Related Topics
What Is the Explanation of This X86 Hello World Using 32-Bit Int 0X80 Linux System Calls from _Start
Dropping of Connections with Tcp_Tw_Recycle
What Is a Reasonable Amount of Inotify Watches with Linux
How to Find Files Modified in Last X Minutes (Find -Mmin Does Not Work as Expected)
What Do the .Eh_Frame and .Eh_Frame_Hdr Sections Store, Exactly
How to Get All Parent Processes and All Subprocesses with 'Pstree'
Unix Vs Bsd Vs Tcp Vs Internet Sockets
Concatenating Two String Variables in Bash Appending Newline
Difference Between Virtual Page and Page Frame
Cross Compiling for Mips Router from X86
How to Copy an Array in Nasm X86 Assembly for Linux, Porting 16-Bit Dos Code
Sh: 0: Getcwd() Failed: No Such File or Directory on Cited Drive
How to Start Tomcat with Output on Console in Linux
How to Dynamically Allocate Memory Using Assembly and System Calls Under Linux
A Running Bash Script Is Hung Somewhere. How to Find Out What Line It Is On