ChatGPT解决这个技术问题 Extra ChatGPT

Copy all files with a certain extension from all subdirectories

Under unix, I want to copy all files with a certain extension (all excel files) from all subdirectories to another directory. I have the following command:

cp --parents `find -name \*.xls*` /target_directory/

The problems with this command are:

It copies the directory structure as well, and I only want the files (so all files should end up in /target_directory/)

It does not copy files with spaces in the filenames (which are quite a few)

Any solutions for these problems?

What about find... exec mv ?
this question actually answered the question i came here to ask. thank you!

B
Brian Agnew

--parents is copying the directory structure, so you should get rid of that.

The way you've written this, the find executes, and the output is put onto the command line such that cp can't distinguish between the spaces separating the filenames, and the spaces within the filename. It's better to do something like

$ find . -name \*.xls -exec cp {} newDir \;

in which cp is executed for each filename that find finds, and passed the filename correctly. Here's more info on this technique.

Instead of all the above, you could use zsh and simply type

$ cp **/*.xls target_directory

zsh can expand wildcards to include subdirectories and makes this sort of thing very easy.


Yes. 'bin it' means to throw it away. Now amended :-)
Bash 4.0+ and ksh93 also supports **. For bash, use shopt -s globstar to enable it. For ksh, it's set -G or set -o globstar.
That exec is technically less efficient than passing into xargs, which will do it all in as few cp calls as possible: find . -name '*.xls' -print0 | xargs -0 cp -t destdir
@Taywee - yes. I didn't want to confuse matters, however, and simply focus on the particular issue. A further efficiency measure is always good
@BrianAgnew Oh, I'm sure, just good to have extra information in the comments for wayward googlers.
g
guya

From all of the above, I came up with this version. This version also works for me in the mac recovery terminal.

find ./ -name '*.xsl' -exec cp -prv '{}' '/path/to/targetDir/' ';'

It will look in the current directory and recursively in all of the sub directories for files with the xsl extension. It will copy them all to the target directory.

cp flags are:

p - preserve attributes of the file

r - recursive

v - verbose (shows you whats being copied)


This is the same as what I had to do when SSH'd into Bluehost.
It does NOT preserver subdirs.
@b005t3r that is by design, it is not supposed to. That was the whole intent of the OP's question. He wanted all files from subdirectories copied into one directory without subdirs
Is there a way to modify this to preserve sub-directories?
@MatthewDean You can use the --parents flag to cp to accomplish that. Something like find ./ -name '*.xsl' -exec cp --parents {} targetDir/ ';'
f
fhdrsdg

I had a similar problem. I solved it using:

find dir_name '*.mp3' -exec cp -vuni '{}' "../dest_dir" ";"

The '{}' and ";" executes the copy on each file.


T
That Guy

I also had to do this myself. I did it via the --parents argument for cp:

find SOURCEPATH -name filename*.txt -exec cp --parents {} DESTPATH \;

C
Camion
find [SOURCEPATH] -type f -name '[PATTERN]' | 
    while read P; do cp --parents "$P" [DEST]; done

you may remove the --parents but there is a risk of collision if multiple files bear the same name.


Q
Quintin van Rooyen

In 2022 the zsh solution also works in Linux Bash:

cp **/*.extension /dest/dir

works as expected.