What does '{} \;' mean in the 'find' command context?
Solution 1:
{}
has absolutely no meaning to bash
, so is passed unmodified as an argument to the command executed, here find
.
On the other hand, ;
has a specific meaning to bash
. It is normally used to separate sequential commands when they are on the same command line. Here the backslash in \;
is precisely used to prevent the semicolon to be interpreted as a command separator by bash
and then allow it to be passed as a parameter to the underlying command, find
. Quoting the semicolon, i.e. ";"
or ';'
, could have been an alternate way to have it stayed unprocessed.
The command:
find ./ -size 0 -exec rm -i {} \;
means: find in the current directory (note that the /
is useless here, .
cannot be but a directory anyway) anything that has a size of 0 and for each object found, run the command rm -i name
, i.e. interactively prompt for each file if you want to remove it. {}
is replaced by each file name found in the executed command. One nice feature is that this file name is strictly a single argument, whatever the file name (even containing embedded spaces, tabs, line feeds, and whatever characters). This wouldn't be the case with xargs
, unless non portable hacks are used. The final ;
is there to end the -exec
clause. The reason why its end needs to be delimited is that other find
options might follow the -exec
one, although it is rarely done. e.g.:
find . -name "*.js" -exec ls -l {} \; -name "special*" -exec wc -l {} \;
One issue with this command is that it won't ignore non plain files, so might prompt the user to delete sockets, block and character devices, pipes, and directories. It will always fail with the latter even if you answer yes.
Another issue, although not really critical here, is that rm
will be called for each file that has a zero size. If you substitute the -exec
ending from /;
to +
, find
will optimize the sub-process creation by only calling rm
the minimal possible number of times, often just once.
Here is then how I would modify this command:
find . -type f -size 0 -exec rm -i {} +
Solution 2:
When using find -exec
, {}
is expanded to each result found.
For example, if you have a directory example
containing 3 files a.txt
, b.txt
and c.txt
, find example/ -exec rm -i {} \;
will expand to:
find example/ -exec rm -i example/a.txt \;
find example/ -exec rm -i example/b.txt \;
find example/ -exec rm -i example/c.txt \;
The \;
at the end is simple an escaped ;
to indicate the end of the exec pattern. Otherwise, it would be interpreted by shell itself.
Solution 3:
In conjunction with the find
command's exec
option, the {}
part is replaced by the name of the files found when the command is executed. The \;
is important too, because that is what defines the end of the command being executed
For instance
find ~ -name \*.bak -exec -rm -f {} \;
would delete all files ending in .bak
anywhere in the user's home directory or in folders contained inside it. By executing rm -f
on each file found.
xargs
takes standard input lines, usually from a pipe, and forms the tailing part of the arguments from it when it executed the command you give it
Solution 4:
To take each case in turn:-
xargs
What this program does is to add each line from input as a parameter to whatever command is passed as its own parameter , then run the resultant command, so if find
lists three files a
, b
and c
in the current directory it would give as output:
./a
./b
./c
If this is piped to xargs rm
, then this command will be executed:
rm ./a ./b ./c
However, if find
also finds a file called " d e "
, then the executed command becomes:
rm ./a ./b ./c ./ d e
To handle this, find
provides an option -print0
, which adds a null character (\0
) after each file, instead of the usual new-line. To tell xargs
that the input is in this form, add the parameter -0
, so the command:
find . ... -print0|xargs -0 rm
will build and execute the command:
rm "./a" "./b" ."/c" "./ d e "
This will work on all file names, including names containing blanks, tabs and new-lines.
find -exec
When this option is called, then what follows (up to \;
) is executed for each file found. This is not terribly useful unless there is some means to refer to the currently found file and that is what {}
does. It can be called more than once in the run string, eg to copy a selection of files to a back-up directory:
find . ... -exec cp "{}" "/BackUp/{}" \;
This in the find
example above would execute in turn:
cp "./a" "/BackUp/./a"
cp "./b" "/BackUp/./b"
cp "./c" "/BackUp/./c"
Note that, outside their use in the find
command, {
and }
have special meanings in bash (multiple commands and expanding lists), but they involve other characters between the brackets: the string {}
is copied literally, so does not need escaping.