GNU xargs is missing the -J option. WHY!?!
I find that using an idiom like
1 |
find foo -maxdepth 1 -print0 | xargs -0 -J % mv % bar/
|
is so useful. It replaces the replstr
(“%” in this example) with all the arguments at once, or as many as can fit without going over the system’s limit. I couldn’t believe it when I learned that the GNU version of xargs lacks this flag. Yes, it’s only on the BSD xargs as far as I can tell.
Every time I’ve searched, someone suggests using the -I
flag on GNU xargs instead, but they are not quite the same. The -I
flag substitutes the replstr
one argument at a time, so that in the earlier example, instead of executing
1 |
mv foo/1 foo/2 foo/3 bar/
|
only once, with the -I
flag it will instead do
1
2
3
|
mv foo/1 bar/
mv foo/2 bar/
mv foo/3 bar/
|
I’ve also tried using the -n
and -L
flags, but they are mutually exclusive with each other and with -I
. OK, so we need some kind of klugey workaround.
1 |
find foo -type f -print0 | cat - < (echo bar/) | xargs -0 -p mv
|
This adds the “bar/” suffix to the standard input before adding it to the end of the mv
command. “But,” you say, “those strings are supposed to be null-terminated!” True, but we’re providing a suffix rather than an extra replacement argument, so the EOF signaled from the input stream is really all we need.
There’s another, more intuitive way, but harder to get right; get the argument list output from a subshell command:
1 |
mv $(find foo -type f) bar/
|
But this suffers from not handling weird file names the right way. Instead one could do:
1 |
echo mv $(/bin/ls --quoting-style=escape foo) bar/
|
This actually works better for file names, but lacks the flexibility of find
.
Is this stuff really what we ought to do? Just give us the -J, GNU. If you know a different way to deal with this, tweet me @realgeek and I’ll update this post.