cp recursive with specific file extension

I am trying to copy all .odt files including those in subdirectories.

cp -r ~/Documents/* ~/copies # works as expected
cp -r ~/Documents/*.odt ~/copies # does not work as expected

The first line will copy all files and in all subdirectories but the second will copy only the .odt files in ~/Documents and none of the files in the subdirectories.


You can either let the (bash) shell do the recursion instead:

shopt -s globstar
cp -nt ~/copies/ ~/Documents/**/*.odt

or use the find command

find ~/Documents -name '*.odt' -exec cp -nt ~/copies/ {} +

The former may exceed the system's ARG_MAX limits if the number if files is very large, whereas the latter will break the command up to stay within ARG_MAX.

I added the -n option to prevent "clobbering" files with the same name.


If you wish to replicate the directory structure (more like what cp -r does as suggested by steeldriver) but only populate it with the .odt files, you can do it with rsync, where -f is the 'filter' option. The other options are more straight-forward. You find all the options in man rsync.

rsync -nvr -f '+ *.odt' -f '+ **/' -f '- *' --prune-empty-dirs ~/Documents/ ~/copies/

It is a good idea to use the 'dry run' option -n and the verbose option -v in order to see what will be done before running the real command (when n is removed from the command line).