Paul
Paul

Reputation: 2710

Is it possible to pipe the results of FIND to a COPY command CP?

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 |?

Upvotes: 46

Views: 40508

Answers (8)

Dylan Callaghan
Dylan Callaghan

Reputation: 108

If you want to preserve the directory structure of the files you are copying (which is useful if you are copying multiple files with the same name in different directories), you can use the --parents and -t options:

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

Upvotes: 0

epetrov
epetrov

Reputation: 61

Try this:

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

In case there is whitespace in filenames.

Upvotes: 6

Jai Jeffryes
Jai Jeffryes

Reputation: 532

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/

Upvotes: 2

Mate
Mate

Reputation: 129

This SOLVED my problem.

find . -type f | grep '\.pdf' |  while read line 
do
        cp $line REPLACE_WITH_TARGET_DIRECTORY
done

Upvotes: 1

Toani
Toani

Reputation: 32

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

Upvotes: 0

fedorqui
fedorqui

Reputation: 289825

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 ...)

Upvotes: 33

glenn jackman
glenn jackman

Reputation: 246867

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

find . -iname "*.SomeExt" | xargs cp -t Directory

Upvotes: 40

Fazlin
Fazlin

Reputation: 2337

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.

Upvotes: 11

Related Questions