42

Is it possible to pipe the results of find to a COPY command cp?

Like this:

find . -iname "*.SomeExt" | cp Destination Directory

Seeking, I always find this kind of formula such as from this post:

find . -name "*.pdf" -type f -exec cp {} ./pdfsfolder \;

This raises some questions:

  1. Why cant you just use | pipe? isn't that what its for?
  2. Why does everyone recommend the -exec
  3. How do I know when to use that (exec) over pipe |?
Community
  • 1
  • 1
Paul
  • 2,620
  • 2
  • 17
  • 27

7 Answers7

37

There's a little-used option for cp: -t destination -- see the man page:

find . -iname "*.SomeExt" | xargs cp -t Directory
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
31

Good question!

  1. why cant you just use | pipe? isn't that what its for?

You can pipe, of course, xargs is done for these cases:

find . -iname "*.SomeExt" | xargs cp Destination_Directory/
  1. Why does everyone recommend the -exec

The -exec is good because it provides more control of exactly what you are executing. Whenever you pipe there may be problems with corner cases: file names containing spaces or new lines, etc.

  1. how do I know when to use that (exec) over pipe | ?

It is really up to you and there can be many cases. I would use -exec whenever the action to perform is simple. I am not a very good friend of xargs, I tend to prefer an approach in which the find output is provided to a while loop, such as:

while IFS= read -r result
do
    # do things with "$result"
done < <(find ...)
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
fedorqui
  • 275,237
  • 103
  • 548
  • 598
9

You can use | like below:

find . -iname "*.SomeExt" | while read line
do
  cp $line DestDir/
done

Answering your questions:

  • | can be used to solve this issue. But as seen above, it involves a lot of code. Moreover, | will create two process - one for find and another for cp.

  • Instead using exec() inside find will solve the problem in a single process.

Fazlin
  • 2,285
  • 17
  • 29
5

Try this:

find . -iname "*.SomeExt" -print0  | xargs -0 cp -t Directory
# ........................^^^^^^^..........^^

In case there is whitespace in filenames.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
epetrov
  • 51
  • 1
  • 2
2

I like the spirit of the response from @fedorqui-so-stop-harming, but it needed a tweak to work in my bash terminal.

In this version...

find . -iname "*.SomeExt" | xargs cp Destination_Directory/

The cp command incorrectly takes Destination_Directory/ as the first argument. I needed to add a replacement string in order to get xargs to insert the argument in the right position for cp. I used a percent symbol for the replacement string, but you can use anything that doesn't conflict with the input from the pipe. This version works for me.

find . -iname "*.SomeExt" | xargs -I % cp % Destination_Directory/
Jai Jeffryes
  • 511
  • 5
  • 12
1

This SOLVED my problem.

find . -type f | grep '\.pdf' |  while read line 
do
        cp $line REPLACE_WITH_TARGET_DIRECTORY
done
Mate
  • 109
  • 2
0

If there are spaces in the filenames, try:

find . -iname *.ext > list.txt
cat list.txt | awk 'BEGIN {a="'"'"'"}{print "cp "a$0a" Directory"}' > script.sh
sh script.sh

You can inspect list.txt and script.sh before sh script.sh. Remember to delete the list.txt and script.sh afterwards.

I had some files with parenthesis and wanted a progress bar, so replaced the cat line with:

cat list.txt | awk -v X='"' '{print "rsync -Pa "X$0X" /Volumes/Untitled/"}' > script.sh

Toani
  • 32
  • 4