Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

find and copy all images in directory using terminal linux mint, trying to understand syntax

OS Linux Mint

Like the title says finally I would like to find and copy all images in a directory.

I found:

find all jpg (or JPG) files in a directory and copy them into the folder /home/joachim/neu2:

find . -iname \*.jpg -print0 | xargs -I{} -0 cp -v {} /home/joachim/neu2

and

find all image files in a direcotry:

find . -name '*' -exec file {} \; | grep -o -P '^.+: \w+ image' 

My problem is first of all, I don't really understand the syntax. Could someone explain the code?

And secondly can someone connect the two codes for generating a code that does what I want ;)

Greetings and thanks in advance!

like image 545
newandlost Avatar asked Jun 16 '14 15:06

newandlost


1 Answers

First, understand that the pipe "|" links commands piping the output of the first into the second as an argument. Your two shell codes both pipe output of the find command into other commands (grep and xargs). Let's look at those commands one after another:

First command: find

find is a program to "search for files in a directory hierarchy" (that is the explanation from find's man page). The syntax is (in this case)

find <search directory> <search pattern> <action>

In both cases the search directory is . (that is the current directory). Note that it does not just search the current directory but all its subdirectories as well (the directory hierarchy).

The search pattern accepts options -name (meaning it searches for files the name of which matches the pattern given as an argument to this option) or -iname (same as name but case insensitive) among others.

The action pattern may be -print0 (print the exact filename including its position in the given search directory, i.e. the relative or absolute path to the file) or -exec (execute the given command on the file(s), the command is to be ended with ";" and every instance of "{}" is replaced by the filename).

That is, the first shell code (first part, left of the pipe)

find . -iname \*.jpg -print0 

searches all files with ending ".jpg" in the current directory hierarchy and prints their paths and names. The second one (first part)

find . -name '*' -exec file {} \; 

finds all files in the current directory hierarchy and executes

file <filename>

on them. File is another command that determines and prints the file type (have a look at the man page for details, man file).

Second command: xargs

xargs is a command that "builds and exectues command lines from standard input" (man xargs), i.e. from the find output that is piped into xargs. The command that it builds and executes is in this case

cp -v {} /home/joachim/neu2"

Option -I{} defines the replacement string, i.e. every instance of {} in the command is to be replaced by the input it gets from file (that is, the filenames). Option -0 defines that input items are not terminated (seperated) by whitespace or newlines but only by a null character. This seems to be necessary when using and the standard way to deal with find output as xargs input.

The command that is built and executed is then of course the copy command with option -v (verbose) and it copies each of the filenames it gets from find to the directory.

Third command: grep

grep filters its input giving only those lines or strings that match a particular output pattern. Option -o tells grep to print only the matching string, not the entire line (see man grep), -P tells it to interpret the following pattern as a perl regexp pattern. In perl regex, ^ is the start of the line, .+ is any arbitrary string, this arbitrary should then be followed by a colon, a space, a number of alphanumeric characters (in perl regex denoted \w+) a space and the string "image". Essentially this grep command filters the file output to only output the filenames that are image files. (Read about perl regex's for instance here: http://www.comp.leeds.ac.uk/Perl/matching.html )

The command you actually wanted

Now what you want to do is (1) take the output of the second shell command (which lists the image files), (2) bring it into the appropriate form and (3) pipe it into the xargs command from the first shell command line (which then builds and executes the copy command you wanted). So this time we have a three (actually four) stage shell command with two pipes. Not a problem. We already have stages (1) and (3) (though in stage (3) we need to leave out the -0 option because the input is not find output any more; we need it to treat newlines as item seperators).

Stage (2) is still missing. I suggest using the cut command for this. cut changes strings py splitting them into different fields (seperated by a delimiter character in the original string) that can then be rearranged. I will choose ":" as the delimiter character (this ends the filename in the grep output, option -d':') and tell it to give us just the first field (option -f1, essentialls: print only the filename, not the part that comes after the ":"), i.e. stage (2) would then be

cut -d':' -f1

And the entire command you wanted will then be:

find . -name '*' -exec file {} \; | grep -o -P '^.+: \w+ image' | cut -d':' -f1 | xargs -I{} cp -v {} /home/joachim/neu2

Note that you can find all the man pages for instance here: http://www.linuxmanpages.com

like image 132
0range Avatar answered Oct 17 '22 04:10

0range