56

If I wanted to copy all *.so files from src to dst I'd do:

cp src/*.so dst 

However, I want to copy all *.so files from src and it's subdirs into dst. Any clues?

1

7 Answers 7

62

Try:

find src/ -type f | grep -i so$ | xargs -i cp {} dst 
7
  • 29
    find can do pattern matching and execute commands. There's no need to pipe its output: find src/ -type f -name '*.so' -exec cp '{}' dst/ ';' Commented Jul 19, 2011 at 15:44
  • 1
    Yes, it can, however it's often easier to read a pipeline, also the pattern matching capabilities of grep far outweigh the simple shell patterns used in the -name parameter to find. xargs is also far more powerful than the -exec parameter to find. Commented Jul 19, 2011 at 15:51
  • 2
    Solution of @jaquer is much better because on MacOSX xargs utility does not have -i option. But his solution works like a charm. Commented Dec 12, 2012 at 13:14
  • I was struggling with an error message on the -i argument to xargs until I read the comment from @bialix regarding jaquer's solution, which worked perfectly on my Mac. Thanks Commented Feb 12, 2018 at 22:11
  • Will this create subdirectories as needed? Commented Nov 8, 2019 at 19:35
25

If you're using Bash, you can turn on the globstar shell option to match files and directories recursively:

shopt -s globstar cp src/**/*.so dst 

If you need to find files whose names begin with ., and/or files in and under directories whose names begin with ., set the dotglob option also (e.g., with shopt -s dotglob).  You can set them both in one command:

shopt -s globstar dotglob 
1
  • 1
    Doesn't copy files in subdirectories starting with a . in their name Commented Jan 8, 2018 at 23:56
18

I tried the command suggested by Mike:

find src/ -type f | grep -i so$ | xargs -i cp {} dst 

but it ended up with dumping all the files into directory dst with their relative paths lost.

To keep the relative paths the command needs to be modified to this:

find src/ -type f | grep -i so$ | xargs -i cp {} dst/{} 
3
  • 11
    +1. However, this assumes that the directories corresponding to the relative source paths exist under dst. In case that's not so, use cp --parents. Also, be sure to quote the filenames. Thus: ... | xargs -i cp --parents "{}" dst (note that the last argument to cp is now "just" the destination directory). The command can be made even shorter (and probably faster) using the -t flag: ... | xargs cp --parents -t dst. Commented Sep 15, 2013 at 20:07
  • 1
    @Stephan202 I had to use ... | xargs cp {} --parents -t dst. Commented Nov 11, 2015 at 7:08
  • 1
    for those on a mac, --parents isn't available. I used ditto in its place: stackoverflow.com/questions/11246070/cp-parents-option-on-mac Commented Oct 29, 2019 at 19:22
10

Another way of doing this is like this:

find src/ -type f -name "*.so" -exec cp {} dst/ \; 
3
  • 1
    And this should reduce number of cp processes: ... -exec cp -t dst/ {} +. Commented Nov 24, 2017 at 13:13
  • Hey Kamil! Thank you for the comment! Can you please explain me how your solution makes things faster! Always willing to learn more :) Commented Nov 25, 2017 at 0:45
  • 3
    When you end -exec with ;, {} is replaced by one path at a time. You get a separate cp for every file. With + syntax find replaces {} with multiple results (up to a system-defined limit of command line length; restriction: {} must be the last thing before +, hence cp -t). Creating and terminating a process is relatively costly thing. For this reason it's good to use + whenever you expect lots of results. In this case copying lots of files might take a lot of time anyway so the difference may pass unnoticed; nevertheless it's a good practice. Commented Nov 25, 2017 at 7:40
2

The grep can be replaced by using find's -name:

find src/ -type f -name "*.so" | xargs -i cp {} dst/{} 
0

For me,

shopt -s globstar cp src/**/*.so dst 

does not copy .so files directly in src. This does:

shopt -s globstar cp src/**/*.so dst cp src/*.so dst 

or this

shopt -s globstar cp src/{**/*,*}.so dst 

But it solves the problem of copying things directly into dst, without preserving paths.

0

For copying from an absolute path, I had to add a sed command.

Also, as --parent did not work properly (cp: with --parents, the destination must be a directory), I had to create the folders first.

find /root/src/ -type f \ | grep -i so$ \ | sed "s|^/root/src/||" \ | sed "s|/\([^/]*\)so$||" \ | xargs -i mkdir --parents /root/dst/{} find /root/src/ -type f \ | grep -i so$ \ | sed "s|^/root/src/||" \ | xargs -i cp /root/src/{} /root/dst/{} 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.