68

I need to copy all *.jar files from a directory maintaining the folder structure of the parent directories. How can I do it in UNIX/Linux terminal?
The command cp -r *.jar /destination_dir is not what I am looking for.

trxgnyp1
  • 317
  • 1
  • 10
Yury Pogrebnyak
  • 4,093
  • 9
  • 45
  • 78
  • does the destination directory need to maintain the folder structure of the source directory? – Alex Mar 08 '12 at 18:45

7 Answers7

112

rsync is useful for local file copying as well as between machines. This will do what you want:

rsync -avm --include='*.jar' -f 'hide,! */' . /destination_dir

The entire directory structure from . is copied to /destination_dir, but only the .jar files are copied. The -a ensures all permissions and times on files are unchanged. The -m will omit empty directories. -v is for verbose output.

For a dry run add a -n, it will tell you what it would do but not actually copy anything.

Sean
  • 15,561
  • 4
  • 37
  • 37
  • 4
    Would you mind explaining me this part: `-f 'hide,! */' `? Thanks in advance for your help! – Matteo Sep 30 '13 at 01:50
  • 10
    Sure. The -f means it is a filter rule (short for --filter). The rule says hide all non-directories, ie files. The '\*/' pattern matches a directory, then the ! negates it to mean anything that is not a directory (so, a file). The '--include=*.jar' has precedence over the filter so .jar files (only) are included. – Sean Sep 30 '13 at 08:10
  • That was very useful, thks!Just to make sure I got it, you are filtering the command by saying to match everything that is not a directory, which means all the files. And the `--include` takes care of making sure that only `.m` files are considered. A last question, what if I were interested in copying **only** the files in that directory, without copying the entire directory structure? – Matteo Sep 30 '13 at 16:32
  • Yes, match all files and hide them, except for .jar files. Not sure I understand your last question: do you mean flattening the tree structure? If so see: http://stackoverflow.com/questions/9800989/copy-every-file-of-entire-directory-structure-into-one-folder Or do you mean copy the .jar files from directory A to dir B? Plain old 'cp' will do that for you, or 'rsync *.jar /path/to/new/dir' – Sean Sep 30 '13 at 18:12
  • Cool, thks again for your explanation. I mean copy all (and only) the `.jar` files in a directory A to a directory B, without going in the sub-directories of A. – Matteo Sep 30 '13 at 22:14
  • This is clearly not the correct answer to this question, I'm not sure why it was marked as accepted. – Ryan Dec 01 '14 at 18:03
  • 1
    @Ryan It's not so clear to at least 44 upvoters. Why is this not the correct answer to this question? – CivFan Aug 26 '15 at 20:55
  • @CivFan I think what Ryan wanted is the flattening of the directories (i.e. copy of all jar files from subdirectories to one folder). This is ambiguous in OP's question as he doesn't say if destination_dir should contain subfolders or be flat. I need it to be flat and question suggested by Sean above has a nice answer by Kaz that worked for me. – alexey Mar 07 '16 at 22:06
  • I had a permission issue, which I initially fixed by doing a sudo, then I realized where "/destination_dir" was in my file system :P – IsMakeFire Jun 30 '17 at 19:48
40

If you don't need the directory structure only the jar files, you can use:

shopt -s globstar
cp **/*.jar destination_dir

If you want the directory structure you can check cp's --parents option.

Smar
  • 8,109
  • 3
  • 36
  • 48
uzsolt
  • 5,832
  • 2
  • 20
  • 32
  • 2
    +1 for shopt globstar, I didn't know about either of those things, useful. – Sean Mar 09 '12 at 10:22
  • 4
    Thanks for pointing out the `--parents` option. It does something slightly different to the posters requirement: It creates the directory structure *as far as needed for the files to copy*, not the whole directory structure. - Was exactly what *I* needed! :-) – halloleo Sep 23 '13 at 01:56
  • 2
    `cp -rf --parents destination_dir` works perfectly – Stuart Cardall Dec 19 '15 at 10:22
20

If your find has an -exec switch, and cp an -t option:

find . -name "*.jar" -exec cp -t /destination_dir {} +

If you find doesn't provide the "+" for parallel invocation, you can use ";" but then you can omit the -t:

find . -name "*.jar" -exec cp {} /destination_dir ";"
user unknown
  • 35,537
  • 11
  • 75
  • 121
  • 1
    If the user's not using GNU find, there will be `-exec` but not with the `+` command terminator. – glenn jackman Mar 08 '12 at 19:57
  • Thanks, @glennjackman. Added a plusless solution. – user unknown Mar 08 '12 at 20:09
  • @glennjackman -1. The + command terminator is defined by POSIX. http://pubs.opengroup.org/onlinepubs/009604599/utilities/find.html – jordanm Mar 08 '12 at 20:18
  • @jordanm, hmm. On a Solaris 8 box at work, the first find in my path is GNU find 4.1, which doesn't have +. Guess I shouldn't take a 10 year old program as gospel. – glenn jackman Mar 08 '12 at 20:31
  • @glennjackman :) 10 years old? But mine is only 4.4.2 - doesn't look so dramatically. – user unknown Mar 08 '12 at 22:23
  • This doesn't recursively copy subdirectories. – lreeder Nov 08 '13 at 19:38
  • @lreeder: It does for me. Gnu-find v. 4.4.2 - just tested. – user unknown Nov 08 '13 at 21:36
  • Hmm, still doesn't work recursively for me. `find -version` says `find (GNU findutils) 4.4.2`. `find . -name "*.jar" -exec cp -t /destination_dir {} +` copies all files flatly to /destination_dir, but not their parent directories. – lreeder Nov 09 '13 at 17:19
  • 1
    @lreeder: No, it doesn't copy the directories which wasn't part of the problem specification but `to copy all *.jar files from directory and all its subdirectories`. – user unknown Nov 09 '13 at 23:26
8
cp --parents `find -name \*.jar` destination/

from man cp:

--parents
       use full source file name under DIRECTORY
Bartosz Moczulski
  • 1,209
  • 9
  • 18
4
tar -cf - `find . -name "*.jar" -print` | ( cd /destination_dir && tar xBf - )
Pavan Manjunath
  • 27,404
  • 12
  • 99
  • 125
  • Why do you use the B flag for the destination tar? The man page says only "reblock as we read". I haven't ever run across obvious issues without it in similar settings, but I'm curious. – Eduardo Ivanec Mar 08 '12 at 23:01
  • @EduardoIvanec Reblock will make sure that the copy has happened correctly. http://linuxdevcenter.com/pub/a/linux/lpt/18_16.html. Also, from tar GNU docs, `If --read-full-records (-B) is used, tar will not panic if an attempt to read a record from the archive does not return a full record. Instead, tar will keep reading until it has obtained a full record` But as you observed, it should run fine in most of the cases. – Pavan Manjunath Mar 09 '12 at 04:46
  • I had an issue with the first tar quitting early due to signal 13 (broken pipe) with this method. Only some of the files were copied. – David Jun 07 '16 at 20:29
4

If you want to maintain the same directory hierarchy under the destination, you could use

(cd SOURCE && find . -type f -name \*.jar -exec tar cf - {} +) \
  | (cd DESTINATION && tar xf -)

This way of doing it, instead of expanding the output of find within back-ticks, has the advantage of being able to handle any number of files.

Idelic
  • 14,976
  • 5
  • 35
  • 40
2
find . -name \*.jar | xargs cp -t /destination_dir

Assuming your jar filenames do not contain spaces, and your cp has the "-t" option. If cp can't do "-t"

find . -name \*.jar | xargs -I FILE cp FILE /destination_dir
glenn jackman
  • 238,783
  • 38
  • 220
  • 352