Understanding the Execution Mechanism of find . -exec … {} \; in Linux

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 Bash and other Shells, the semicolon ; itself is a command separator, used to write multiple commands in one line. For example, ls; pwd.

  • If you write the semicolon ; directly, the Shell will parse it before the find command, thinking it is two independent 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 escape it, 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 a normal 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 -exec starts from ls and ends at \;. It will execute ls -l <filename> for each found .txt file.
    • Tip: find can have multiple -exec options.

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/

Leave a Comment