32

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?

38

Try:

find src/ -type f | grep -i so$ | xargs -i cp {} dst
  • 17
    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/ ';' – jáquer Jul 19 '11 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. – Mike Insch Jul 19 '11 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. – bialix Dec 12 '12 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 – svannoy Feb 12 '18 at 22:11
  • Will this create subdirectories as needed? – Lennart Rolland Nov 8 '19 at 19:35
16

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
  • Doesn't copy files in subdirectories starting with a . in their name – rboy Jan 8 '18 at 23:56
8

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/{}
  • 7
    +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. – Stephan202 Sep 15 '13 at 20:07
  • 1
    @Stephan202 I had to use ... | xargs cp {} --parents -t dst. – warvariuc Nov 11 '15 at 7:08
  • for those on a mac, --parents isn't available. I used ditto in its place: stackoverflow.com/questions/11246070/cp-parents-option-on-mac – Bryan Young Oct 29 '19 at 19:22
3

Another way of doing this is like this:

find src/ -type f -name "*.so" -exec cp {} dst/ \;
  • And this should reduce number of cp processes: ... -exec cp -t dst/ {} +. – Kamil Maciorowski Nov 24 '17 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 :) – Kyriakos Nov 25 '17 at 0:45
  • 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. – Kamil Maciorowski Nov 25 '17 at 7:40
1

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.