When we use the -exec option, according to the man[1] manual, there are two ways to end the command:
# Ends with \\;
find . -name "*.txt" -exec ls -l {} \;
# Or ends with +
find . -name "*.txt" -exec ls -l {} +
The manual mentions that the command must end with ; or +.
;needs to be escaped (written as\;or';'),- while
+does not need to be escaped.
1. Using \;: Executes the command once for each matching file found.
find . -name "*.txt" -exec ls -l {} \;
2. Using +: Passes as many matching files as possible as arguments to the command at once, similar to xargs. This greatly improves efficiency by reducing the number of process starts.
find . -name "*.txt" -exec cp {} /backup/ +
# This command will execute like this: cp file1.txt file2.txt ... fileN.txt /backup/
1. Why the semicolon needs to be escaped (written as \; or ';'):
-
In
Bashand otherShells, the semicolon;itself is acommand separator, used to write multiple commands in one line. For example,ls; pwd. -
If you write the semicolon
;directly, theShellwill parse it before thefindcommand, thinking it is twoindependent commands:find . -name "*.txt" -exec ls -l {} # First "command", incomplete, will report an error ; # Second "empty command"Not ending with
;, the command output result:$ find . -name "*.txt" -exec ls -l {} find: -exec: no terminating ";" or "+" # Error message must terminate with ; or +. -
To prevent the Shell from interpreting this semicolon, we need to
escapeit, which means stripping it of its special meaning. There are two methods: - Using a backslash to escape:
\;. This is the most common method. The backslash tells the Shell: “the following character is just anormal semicolon, with no special function”. - Using quotes to surround:
';'or";". Quotes can also serve to shield special characters.
Summary
When typing find . -name "*.txt" -exec ls -l {} \; into the Shell, what happens is:
Shell parsing: The Shell sees the backslash plus semicolon\;, treating it as a normal semicolon character, and then passes the entire string as a whole to the find command.find parsing: The find command, upon receiving the parameters, knows that-execstarts fromlsand ends at\;. It will executels -l <filename>for each found.txtfile.- Tip: find can have multiple
-execoptions.
2. An Important Alternative: Plus +
Initially, -exec ended with \;, until the findutils[2] toolkit was updated to 4.2.12 to support the -exec ... + option.
The differences between the two are as follows:
| Feature | -exec ... {} \; |
-exec ... {} + |
|---|---|---|
| Execution Count | Executes the command once for each file found | Passes as many files as arguments as possible, executing the command at once |
| Processes | Starts N cmd processes (N=number of files) |
Usually starts only 1 cmd process |
| Efficiency | Low, especially with many files | High, greatly reduces process overhead |
| Syntax | Semicolon (;) must be escaped |
Plus (+) does not need to be escaped |
Assuming 3 files are found:a.txt, b.txt, c.txt.
-exec ls -l {} \;will execute:
ls -l a.txt
ls -l b.txt
ls -l c.txt
# A total of 3 commands
-exec ls -l {} +will execute:
ls -l a.txt b.txt c.txt
# Only 1 command
As you can see, it can somewhat replace xargs.
References
[1]
man: https://man.archlinux.org/man/find.1
[2]
findutils: https://www.gnu.org/software/findutils/