Question

I want to do something similar to this:

find . -type f | xargs cp dest_dir

But xargs will use dest_dir as initial argument, not as final argument. I would like to know:

  1. Is it possible to specify final arguments to xargs?
  2. Any other alternative to achieve the same result?

EDIT

A possible, but cumbersome alternative is this:

find . -type f | while read f ; do echo cp $f dest_dir ; done

I do not like this because dozens of cp processes will be started.

Was it helpful?

Solution

-I option replaces occurrences of replace-str in the initial-arguments with names read from standard input.

For example:

find . -type f | xargs -I file cp file dest_dir

Should solve your problem.

More details in the following tutorial:

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/

Especially the part about {} as the argument list marker

OTHER TIPS

I was trying to achieve something slightly different using xargs and pdfunite, and launching multiple instances of pdfunite didn't cut it for me.

I was essentially trying to do:

$ ls -v *.pdf | xargs pdfunite <files> all-files.pdf

with all-files.pdf being the final argument to pdfunite. But xargs with -I didn't give me what I wanted:

$ ls -v *.pdf | xargs -I {} pdfunite {} all-files.pdf

This just gave me a result which had all-files.pdf being the last file output by ls. And that's because multiple instances of pdfunite were launched.

I finally just gave up on xargs, and went with the following simpler construct, which sufficed for my purposes:

$ pdfunite `ls -v *.pdf` all-files.pdf

Hope this helps someone in future.

Usually, you don't need to use xargs (for example, find . -type f -exec cp {} dest_dir +). But just in case you do ...


Simply save the below script in $PATH, and use it like:

find . -type f | xargs final-args dest_dir -- cp

Script:

#!/bin/bash
# Usage: final-args 4 5 6 -- echo 1 2 3
final=()
for arg
do
    shift
    if [[ "$arg" = "--" ]]
    then
        break
    fi
    final+=("$arg")
done
exec "$@" "${final[@]}"

As an alternative solution, use -t when the command supports it. cp and mv have it.

find -type f -print0 | xargs -0 cp -t dest_dir --
find -type f -exec cp -t dest_dir -- {} +

As noted by o11c, using xargs in this case is not necessary.

Also notice that I used -print0 and -0 in my first example. Using '\0' as a delimiter avoids trouble with filenames having newlines with them.

Everyone here also didn't suggest to at least use -d '\n'. xargs splits spaces (not just newlines) when it is not specified.

find -type f | xargs -d '\n' cp -t dest_dir --

GNU Parallel handles this better than xargs, avoiding the workarounds above, if you have that available.

http://www.gnu.org/software/parallel/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top