28

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.

blueFast
  • 41,341
  • 63
  • 198
  • 344

5 Answers5

31

-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

Darth Hunterix
  • 1,484
  • 5
  • 27
  • 31
  • 10
    However, `-I` implies `-L 1`, that means it is still suffering from the problem OP tried to avoid (*"I do not like this because dozens of cp processes will be started."*) – Adrian Shum Mar 04 '16 at 06:25
4

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.

Praveen
  • 6,872
  • 3
  • 43
  • 62
3

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 --
konsolebox
  • 72,135
  • 12
  • 99
  • 105
  • 1
    This is a great suggestion for GNU `cp` and `mv`, but I don't think it's portable. For example, BSD utilites (at least the ones on the Mac OS X machines I manage) do not support the -t flag. (Only the `-i` and `-f` flags are POSIX). – drwatsoncode Jul 19 '18 at 20:02
2

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[@]}"
o11c
  • 15,265
  • 4
  • 50
  • 75
0

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

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

Joshua Goldberg
  • 5,059
  • 2
  • 34
  • 39