In our earlier NameHero tutorial on the find command, I briefly mentioned that you can use “exec” to execute commands on each file found. The full truth is that “exec” is used extremely often with find, and it’s one of the most common command patterns in Linux. The ability to get a list of files and execute commands on each of them is extremely powerful, and it can turn hours of work into just a few seconds. But with that power comes the potential for wasting resources. If your find command locates just a few files, it’s unlikely to make a big difference, unless your “exec” commands themselves are huge. But if you’re cleaning up a server, for example, and trying to back up half a million files, then efficiency plays a huge role in how you use the find and exec command together.
And that’s where the “+” and “;” terminators come into play.
Basic Anatomy of a find -exec Command
As mentioned in my earlier tutorial, we can use the exec command with the find command like this:
find /path/to/search -name "*.log" -exec rm {} \;
In this example, the “find” command locates all files ending with the “.log” extension, and then uses the “-exec” flag to remove them one by one. The {} brackets are a stand-in for each file that is found. The semi-colon at the end needs to be escaped, since it’s also a special character in the batch syntax.
The syntax is easy to understand, and as mentioned, it doesn’t make a huge difference if you’re only dealing with a handful of files.
Single vs Multiple Processes
But let’s say you have hundreds of thousands of files that match the pattern in “find”. In that case, the above command is highly inefficient because of the way “exec” works.
Spawning a New Process for Each File with ;
When you use the semi-colon (;) with the exec command, it spawns a new process for each file in find. So, for example, let’s say the following command:
find /path/to/search -name "*.log" -exec rm {} \;
Found three files:
file1.log
file2.log
file3.log
Then, find would execute the following three commands separately:
rm file1.log
rm file2.log
rm file3.log
As you can imagine, this can scale up problematically, the more files you find. If you’re doing a major cleanup on the server, it can use up tons of resources.
Using the Same Process for All the Files
Conversely, you can use the plus (+) sign at the end of the exec command to execute a batch process of all the files that are found. So the following command:
find /path/to/search -name "*.log" -exec rm {} +
Will run this single command:
rm file1.log file2.log file3.log
Now, your system might have an upper limit for the number of arguments it’s allowed for a command to have, so the find command will respect that and only append as many arguments as it’s allowed at a given time, but it will use as few “rm” commands as possible. As you can imagine, this is significantly faster than spawning a new process for each file.
For a small number of files, it makes practically no difference whether you use the semi-colon or the plus sign, but there are situations where you will find large numbers of files, and in such situations, using the plus sign is simply a much better option with no downsides.
So then why don’t we always use the plus sign? Why does the semi-colon syntax even exist in the first place? Turns out, there are many times when we need to execute the command separately.
When to use the Semi-Colon with the find -exec Command
Despite the undeniable benefits of using the plus sign, the semi-colon has some use cases where we must use it instead.
When We Need to Use the {} Argument in the Middle of the Command
One of the requirements of using the “plus” sign with find -exec is that the argument must be the last string before the plus sign. In our previous example:
find /path/to/search -name "*.log" -exec rm {} +
Note that the square brackets were the end of the command, and the plus sign came immediately after that. If, on the other hand, we had a command where the argument came in the middle, like this:
find . -name "*.log" -exec mv {} /var/log/archive/ \;
Then we need to use the semicolon instead. This particular example might vary if your Linux flavor has a slightly different implementation of the “mv” command, but in general, custom dictates that the destination argument must come at the end. In this command, we have to specify the file name in the middle of the command, so we can’t use the plus sign, no matter how much it would benefit us.
In some scenarios, you might be able to use a bit of ingenuity to rewrite the command such that you can specify the argument at the end. For example, you can fiddle with creating shell wrappers to rewrite the above command, but it’s complicated. Still, if the number of files is prohibitively large, you might find it worth exploring how to do this.
When we Need Multiple {} Arguments
Another requirement of using the plus sign is that the “{}” argument must be used exactly once in the command. So the following command requires a semi-colon:
find . -name "*.conf" -exec cp {} {}.bak \;
In this example, we’re using the placeholder argument twice in the same command. Of course, in this case, we also don’t have it at the end of the command, but even if we did, it wouldn’t work. We would need to rewrite the command. But rewriting this command to use the {} argument just once is even harder than rewriting a command to place the argument at the end. And the performance hit you might need to take to do so could negate the benefits of using the plus sign in the first place.
Command that can Accept Just One Argument
The plus sign is powerful because it creates a single command with multiple arguments, instead of multiple commands with single arguments. But what if the command itself doesn’t allow for multiple arguments?
The “ffmpeg” command is one such example, where it can only accept a single argument as the output.
The “find” command is blind to the requirements of the command you’re using with “exec”. If it accepts only a single argument, it will try to append the entire list of files to the end, causing the command to either crash with an error saying you have too many arguments, or it might end up ignoring all the arguments except for the first one.
Either way, not a good outcome.
Conclusion
The “exec” command is used very frequently with the “find” command to execute instructions against each file that’s found. Instead of executing a separate command for each file with the semi-colon at the end, we can instruct “find” to create a single command with all the arguments appended at the end. If your command supports such a syntax, the find command is much more efficient with the “plus” sign than with the semicolon at the end. You just need to be careful about a few caveats, such as not being able to use multiple arguments and the requirement to place the single argument right at the end of the command.

I’m a NameHero team member, and an expert on WordPress and web hosting. I’ve been in this industry since 2008. I’ve also developed apps on Android and have written extensive tutorials on managing Linux servers. You can contact me on my website WP-Tweaks.com!

Leave a Reply