Xargs – Argument Line Too Long Error with Find

argumentsxargs

I have a line like the following:

find /foo/bar -name '*.mp4' -print0 | xargs -i {} -0 mv -t /some/path {}

but I got the following error:

xargs: argument line too long

I am confused. Isn't the use of xargs supposed to precisely help with this problem?

Note: I know that I can techincally use -exec in find, but I would like to understand why the above fails, since my understanding is that xargs is supposed to know how to split the input into a manageable size to the argument that it runs. Is this not true?

This is all with zsh.

Best Answer

Well for one thing the -i switch is deprecated:

-i[replace-str]
     This  option  is a synonym for -Ireplace-str if replace-str is specified. 
     If the replace-str argument is missing, the effect is the same as -I{}. 
     This option is deprecated; use -I instead.

So when I changed your command around to this, it worked:

$ find /foo/bar -name '*.mp4' -print0 | xargs -I{} -0 mv -t /some/path {}

Example

$ find . -print0 | xargs -I{} -0 echo {}
.
./.sshmenu
./The GIT version control system.html
./.vim_SO
./.vim_SO/README.txt
./.vim_SO/.git
./.vim_SO/.git/objects
./.vim_SO/.git/objects/pack
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.idx
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.pack
./.vim_SO/.git/objects/info
./.vim_SO/.git/refs
./.vim_SO/.git/refs/tags
...

Use of -I{}

This approach shouldn't be used since running this command construct:

$ find -print0 ... | xargs -I{} -0 ...

implicitly turns on these switches to xargs, -x and -L 1. The -L 1 configures xargs so that it's calling the commands you want it to run the files through in a single fashion.

So this defeats the purpose of using xargs here since if you give it 1000 files it's going to run the mv command 1000 times.

So which approach should I use then?

You can do it using xargs like this:

$ find /foot/bar/ -name '*.mp4' -print0 | xargs -0 mv -t /some/path

Or just have find do it all:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +
Related Question