LinuxQuestions.org
Latest LQ Deal: Complete CCNA, CCNP & Red Hat Certification Training Bundle
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices


Reply
  Search this Thread
Old 09-10-2009, 03:24 PM   #1
rjo98
Senior Member
 
Registered: Jun 2009
Location: US
Distribution: RHEL, CentOS
Posts: 1,697

Rep: Reputation: 48
How to recursively copy certain file types


How would i go about copying all .jpg or .JPG files from a folder and all its subfolders to my /usr/name/pictures folder? I'm guessing I'd have to use some sort of .[jJ][pP][gG] to get all the pictures from other examples i've seen, but really not sure how to use that in a recursive cp.
 
Old 09-10-2009, 03:40 PM   #2
w1k0
Senior Member
 
Registered: May 2008
Location: Poland
Distribution: Slackware, Mint
Posts: 1,252

Rep: Reputation: 205Reputation: 205Reputation: 205
find . -name *.[jJ][pP][gG] -exec cp {} /usr/name/pictures/ \;

or:

find . -iname *.jpg -exec cp {} /usr/name/pictures/ \;

Last edited by w1k0; 09-10-2009 at 03:43 PM.
 
Old 09-10-2009, 03:49 PM   #3
rjo98
Senior Member
 
Registered: Jun 2009
Location: US
Distribution: RHEL, CentOS
Posts: 1,697

Original Poster
Rep: Reputation: 48
thanks. what does the \; at the end of the line do?
 
Old 09-10-2009, 03:56 PM   #4
rjo98
Senior Member
 
Registered: Jun 2009
Location: US
Distribution: RHEL, CentOS
Posts: 1,697

Original Poster
Rep: Reputation: 48
also, do i need to do a /usr/name/pictures/ or /usr/name/pictures/{}

or does it not really matter?
 
Old 09-10-2009, 04:13 PM   #5
i92guboj
Gentoo support team
 
Registered: May 2008
Location: Lucena, Córdoba (Spain)
Distribution: Gentoo
Posts: 4,074

Rep: Reputation: 386Reputation: 386Reputation: 386Reputation: 386
I would take the command outside the find. With such scheme cp is run once for each file which is an utter waste of resources. It's better to pipe it out and use xargs. More efficient.

Code:
find . -type f -iname \*.jpg | xargs -i mv '{}' /new/path

Last edited by i92guboj; 09-10-2009 at 04:15 PM. Reason: simplified a bit
 
Old 09-10-2009, 04:17 PM   #6
rjo98
Senior Member
 
Registered: Jun 2009
Location: US
Distribution: RHEL, CentOS
Posts: 1,697

Original Poster
Rep: Reputation: 48
thanks again i92, you're turning out to be my hero today!

I'm not sure what xargs is though.

the '{}' just means all the results from the find, right?
 
Old 09-10-2009, 04:28 PM   #7
i92guboj
Gentoo support team
 
Registered: May 2008
Location: Lucena, Córdoba (Spain)
Distribution: Gentoo
Posts: 4,074

Rep: Reputation: 386Reputation: 386Reputation: 386Reputation: 386
Quote:
Originally Posted by rjo98 View Post
thanks again i92, you're turning out to be my hero today!

I'm not sure what xargs is though.
xargs takes all the arguments you supply (usually via a pipe '|' ), and then pass them to the specified command. In this case, it takes the list of files fount by the "find" command, and build mv commands using them.

The magic behind xargs is greater than you think. In linux, a command has a max length, so xargs makes sure that the mv command is within these limits. If it gets too large, xargs spawns another new mv with more files, and so and so until all the elements from the list are consumed.

This is why xargs will always work, while in-lining (example below) will fail if the list is too big:

Code:
mv $(find . -type f -iname \*.jpg) /new/path
# will fail if there are too much files
# better use xargs
Quote:
the '{}' just means all the results from the find, right?
It mimics the syntax from find -exec, but it's not the same thing. find and xargs are separate commands, we are just chaining them. Usually '{}' means nothing outside find -exec. xargs however has the power to reference the pipe by using it if you use -i. It's not always necessary, for example, if the file list is the last thing that you would put in the command, then you don't need the -i '{}':

Code:
find . -type f -iname \*.jpg | xargs echo -n

Last edited by i92guboj; 09-10-2009 at 04:31 PM.
 
Old 09-10-2009, 04:31 PM   #8
rjo98
Senior Member
 
Registered: Jun 2009
Location: US
Distribution: RHEL, CentOS
Posts: 1,697

Original Poster
Rep: Reputation: 48
thanks for explaining. so if i just wanted to copy the files rather than move them (deleting them from the source) i would just change the mv to a cp and it will work the same? I guess just as a backup in case i do something wrong, i wont have killed the files in their original location.
 
Old 09-10-2009, 04:34 PM   #9
i92guboj
Gentoo support team
 
Registered: May 2008
Location: Lucena, Córdoba (Spain)
Distribution: Gentoo
Posts: 4,074

Rep: Reputation: 386Reputation: 386Reputation: 386Reputation: 386
Right.
 
Old 09-10-2009, 04:49 PM   #10
karlatLQ
Member
 
Registered: Sep 2009
Posts: 67

Rep: Reputation: 19
From the man pages for find, you will find many options that you can use.
> man find

Quote:
-iname pattern
Like -name, but the match is case insensitive. For example, the patterns ‘fo*’ and ‘F??’ match the file names ‘Foo’, ‘FOO’, ‘foo’, ‘fOo’, etc. In these patterns, unlike filename expansion by the shell, an initial ’.’ can be matched by ‘*’. That
is, find -name *bar will match the file ‘.foobar’. Please note that you should quote patterns as a matter of course, otherwise the shell will expand any wildcard characters in them.
Quote:
-exec command {} +
This variant of the -exec action runs the specified command on
the selected files, but the command line is built by appending
each selected file name at the end; the total number of invoca-
tions of the command will be much less than the number of
matched files. The command line is built in much the same way
that xargs builds its command lines. Only one instance of ‘{}’
is allowed within the command. The command is executed in the
starting directory.
The second command will find the .jpg and .JPG and .jPg and .jPG ... files and then copy each one to the directory. There is no reason to add {} after the directory name that you want to copy the files to. The command as it was given is the way that you would use it to answer your post question. The exception being that you can enclose the {} in single quote marks to protect them. You can also have used xargs to do the same thing as the -exec command.

Quote:
find . -iname *.jpg -exec cp {} /usr/name/pictures/ \;
find . -iname *.jpg -exec cp '{}' /usr/name/pictures/ \;

find . -iname *.jpg -type f print0 | xargs --null cp --target-directory=/usr/name/pictures/
 
Old 09-10-2009, 05:31 PM   #11
rjo98
Senior Member
 
Registered: Jun 2009
Location: US
Distribution: RHEL, CentOS
Posts: 1,697

Original Poster
Rep: Reputation: 48
so I'm confused now, do i need to put a backslash before that *.jpg or no if i use i92's line?

find . -type f -iname \*.jpg | xargs -i mv '{}' /new/path
 
Old 09-10-2009, 05:53 PM   #12
karlatLQ
Member
 
Registered: Sep 2009
Posts: 67

Rep: Reputation: 19
Quote:
Originally Posted by rjo98 View Post
so I'm confused now, do i need to put a backslash before that *.jpg or no if i use i92's line?

find . -type f -iname \*.jpg | xargs -i mv '{}' /new/path
No.
Try this, which will not copy any of the files, just prints them out. If you have a ton of jpg files, then create a new directory and just put a few there to try it.

Quote:
find . -iname *.jpg -type f -print


You can also do it this way:
Quote:
find . -type f -iname *.jpg -print


This will just print the files that it finds. Then, when you find that it works right on your shell, then you can move them.

Quote:
find . -iname *.jpg -type f -exec mv '{}' /new/path \;
 
Old 09-10-2009, 05:55 PM   #13
rjo98
Senior Member
 
Registered: Jun 2009
Location: US
Distribution: RHEL, CentOS
Posts: 1,697

Original Poster
Rep: Reputation: 48
so i guess that means the backslash before the *.jpg is optional?
 
Old 09-10-2009, 05:55 PM   #14
i92guboj
Gentoo support team
 
Registered: May 2008
Location: Lucena, Córdoba (Spain)
Distribution: Gentoo
Posts: 4,074

Rep: Reputation: 386Reputation: 386Reputation: 386Reputation: 386
No. It's not optional.

Yes, you need it. You need to scape these so they don't get expanded by bash before find can (hehe) find them.

If you do "find . -name *" on a dir containing two files 'a' and 'b', this command is expanded to

Code:
find . -name a b
Which will obviously end in an error except in one case: when '*' is expanded to just one element. But even in that case, most likely, the output might not be what you wanted in first place.

If you use a backslash to scape it, the command is not expanded, and find receives the '*' as-is, and is free to handle it by itself. You could as well use single quotes to scape the whole string, instead of escaping the asterisk.
 
Old 09-10-2009, 05:58 PM   #15
i92guboj
Gentoo support team
 
Registered: May 2008
Location: Lucena, Córdoba (Spain)
Distribution: Gentoo
Posts: 4,074

Rep: Reputation: 386Reputation: 386Reputation: 386Reputation: 386
Here you can see what I mean illustrated:

Code:
$ ls -R
.:
1  a  b

./1:
c  d
$ find . -name *
find: paths must precede expression: a
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
$ cd 1
$ find . -name *
find: paths must precede expression: d
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
# This one works because we have only ONE jpg file.
$ touch foo.jpg
$ find . -name *.jpg
./foo.jpg
$ touch bar.jpg
# But the same command will fail if the * is expanded to two
# or more files, just try it.
$ find . -name *.jpg
find: paths must precede expression: foo.jpg
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
# Either escaping or quoting works.
$ find . -name '*.jpg'
./foo.jpg
./bar.jpg
$ find . -name \*.jpg
./foo.jpg
./bar.jpg
Conclusion: always quote your asterisks or escape them when using -name in find.

Last edited by i92guboj; 09-10-2009 at 06:01 PM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
copy recursively across directories stefaandk *BSD 1 11-09-2008 11:01 PM
how to copy images recursively via bash mindfriction Programming 6 12-13-2007 11:25 PM
Using cp to copy directory permissions recursively? SirTristan Linux - Newbie 3 11-25-2007 09:36 AM
trying to recursively copy jpegs DJOtaku Linux - General 10 08-26-2005 02:09 PM
recursively copy php.ini to www directories osio Programming 3 07-01-2005 05:24 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie

All times are GMT -5. The time now is 10:23 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration