Reputation: 45
I have a /dir1
, the structure is:
./aaa.txt
./bbb.jpg
./subdir1/cccc.doc
./subdir1/eeee.txt
./subdir2/dddd.xls
./subdir2/ffff.jpg
I want to copy all .txt
and .jpg
in /dir1
and its subdirectories to /dir2
.
By using cp -r /dir1/* /dir2
, it copies all files and its structure to /dir2
.
By using cp -r /dir1/*.jpg /dir2
and cp -r /dir1/*.txt /dir2
, it doesn't copy the .jpg
and .txt
files in the subdirectories.
By using
for FILE in `find`
do
cp -f $FILE /dir1/
done
it can copy all files in dir and subdirectories to the destination, but I cannot filter the file type.
Is there another way that can achieve what I want?
Upvotes: 2
Views: 5535
Reputation: 52536
With the globstar
1 and extglob
shell options:
shopt -s globstar extglob
cp dir1/**/*.@(txt|jpg) dir2
The **/
expression matches zero or more directories or subdirectories, so you get files from the whole subtree of dir1
; @(txt|jpg)
is an extended pattern that matches either txt
or jpg
.
For the somewhat unlikely case that there are no .jpg
and .txt
files in any of dir1
, but a file called *.@(txt|jpg)
in a subdirectory of dir1
called **
, you may also want to set the failglob
shell option so the command is not executed if the glob doesn't match anything – instead of copying the unlikely file.
1 globstar
was added in Bash 4.0 and is thus not available for the Bash that comes standard with Mac OS (3.2).
Upvotes: 1
Reputation: 42147
You can use find
to get the desired source files, and then use cp
within the -exec
predicate of find
to copy the files to the desired destination. With GNU cp
:
find /dir1/ -type f \( -name '*.txt' -o -name '*.jpg' \) -exec cp -t /dir2/ {} +
POSIX-ly:
find /dir1/ -type f \( -name '*.txt' -o -name '*.jpg' \) -exec cp {} /dir2/ \;
Upvotes: 5