Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does find -exec mv {} ./target/ + not work?

I want to know exactly what {} \; and {} \+ and | xargs ... do. Please clarify these with explanations.

Below 3 commands run and output same result but the first command takes a little time and the format is also little different.

find . -type f -exec file {} \; find . -type f -exec file {} \+ find . -type f | xargs file 

It's because 1st one runs the file command for every file coming from the find command. So, basically it runs as:

file file1.txt file file2.txt 

But latter 2 find with -exec commands run file command once for all files like below:

file file1.txt file2.txt 

Then I run the following commands on which first one runs without problem but second one gives error message.

find . -type f -iname '*.cpp' -exec mv {} ./test/ \; find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec' 

For command with {} \+, it gives me the error message

find: missing argument to `-exec' 

why is that? can anyone please explain what am I doing wrong?

like image 324
Shahadat Hossain Avatar asked Apr 09 '11 20:04

Shahadat Hossain


People also ask

How does the mv command work?

The mv command moves files and directories from one directory to another or renames a file or directory. If you move a file or directory to a new directory, it retains the base file name. When you move a file, all links to other files remain intact, except when you move it to a different file system.

Does mv overwrite by default?

By default the mv command will overwrite an existing file.


2 Answers

The manual page (or the online GNU manual) pretty much explains everything.

find -exec command {} \;

For each result, command {} is executed. All occurences of {} are replaced by the filename. ; is prefixed with a slash to prevent the shell from interpreting it.

find -exec command {} +

Each result is appended to command and executed afterwards. Taking the command length limitations into account, I guess that this command may be executed more times, with the manual page supporting me:

the total number of invocations of the command will be much less than the number of matched files.

Note this quote from the manual page:

The command line is built in much the same way that xargs builds its command lines

That's why no characters are allowed between {} and + except for whitespace. + makes find detect that the arguments should be appended to the command just like xargs.

The solution

Luckily, the GNU implementation of mv can accept the target directory as an argument, with either -t or the longer parameter --target. It's usage will be:

mv -t target file1 file2 ... 

Your find command becomes:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+ 

From the manual page:

-exec command ;

Execute command; true if 0 status is returned. All following arguments to find are taken to be arguments to the command until an argument consisting of `;' is encountered. The string `{}' is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find. Both of these constructions might need to be escaped (with a `\') or quoted to protect them from expansion by the shell. See the EXAMPLES section for examples of the use of the -exec option. The specified command is run once for each matched file. The command is executed in the starting directory. There are unavoidable security problems surrounding use of the -exec action; you should use the -execdir option instead.

-exec command {} +

This variant of the -exec action runs the specified command on the selected files, but the command line is built by appending each selected file name at the end; the total number of invocations of the command will be much less than the number of matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of `{}' is allowed within the command. The command is executed in the starting directory.

like image 77
Lekensteyn Avatar answered Nov 12 '22 04:11

Lekensteyn


I encountered the same issue on Mac OSX, using a ZSH shell: in this case there is no -t option for mv, so I had to find another solution. However the following command succeeded:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \; 

The secret was to quote the braces. No need for the braces to be at the end of the exec command.

I tested under Ubuntu 14.04 (with BASH and ZSH shells), it works the same.

However, when using the + sign, it seems indeed that it has to be at the end of the exec command.

like image 22
arvymetal Avatar answered Nov 12 '22 04:11

arvymetal