7

I am trying to resize all files that are found using a FIND command (they are all files within a directory and its subdirectories). I tried many options but stumble across different errors each time. This is my last attempt:

find /my/folder/ -name '*jpg' -exec 'mogrify -resize 900">"{}' \;

I tried many others but to no avail. What goes wrong?

4 Answers4

11

I don't like exec on find very much, it's a bit ancient and unpredictable specially when you feed it all the quotes :)

Try using xargs, it should behave a bit more civilized

find /my/folder/ -name '*jpg' -print0 | xargs -0 -J% "mogrify -resize 900 > %"

Things that are different here

-print0 on find appends a NULL character after each filename as separator

in xargs

-0 tells that the input is separated by NULL char -J% says that % should be replaced by the content in the input (filename in this case)

lynxman
  • 9,397
  • 3
  • 25
  • 28
  • Ha this time you beat me @lynxman! I seriously have to write a blog post '-exec considered harmful`. Oh and I will give you an upvote in a couple hours after my daily limit expires. – Phil Hollenback Jan 28 '11 at 21:55
  • Thanks! But I get: xargs: invalid option -- 'J' Also, mogrify -resize 900">"foo.jpg will resize it only IF wider than 900 pixels (therefore the ">" between quotes. –  Jan 28 '11 at 21:58
  • 1
    On my xargs here you'd want `xargs -0 -I% mogrify -resize 900">" %` – DerfK Jan 28 '11 at 22:00
  • bonus: If you add -Px (where x is an integer) it will use that many processes to run the command (so you can take advantage of multiple cores) – mikelikespie Jan 29 '11 at 03:35
  • 1
    mikelikespie see my answer for an alternative way to do the same thing a bit more awesomely via gun parallel. – Phil Hollenback Jan 29 '11 at 06:21
7

@lynxman nailed the traditional advice of find/xargs. However another alternative is to use find with gnu parallel. Parallel offers some neat enhancements to xargs like more sophisticated argument handling and the ability to run many jobs in parallel (I realize that some newer versions of xargs do support parallel jobs too).

So, lets see if we can rewrite this in parallel. How about:

find /my/folder -name '*jpg' -print0 | parallel --progress -0 -j +0 "mogrify -resize 900\> {}"

that will find all jpeg files and run them through parallel. parallel will run as many jobs in parallel as you have cpu cores (-j +0).

If you are processing a lot of files, you could gain some significant time savings by intelligently batching the jobs like this. The --progress option displays some details as the job is running.

The -print0 from the find command uses nulls to separate the output, in case you have spaces or weird characters in your filenames. The -0 option to parallel expects input separated with nulls. If you are sure your input filenames don't contain spaces, you can omit -print0 / -0. Note that most versions of xargs also takes the -0 option so you can use this trick with xargs as well as with parallel.

EDIT: From comments I didn't get the commands quite right. Adjusted mogrify now that I actually tried to use it. Don't need to save to new files because mogrify writes to the existing file by default.

Phil Hollenback
  • 14,947
  • 4
  • 35
  • 52
  • Amazing work Phil! \o/ – lynxman Jan 28 '11 at 22:38
  • 1
    Thanks for the education here! I tried to make it would and couldn't, though. The -0 flag is a tipo I guess in the parallel command, but even without that it just prints the commands with no results. BTW: with mogrify -resize">"200 you resize an image to 200 ONLY if it is larger than 200 (so only decreasing, no increasing). VERY useful. –  Jan 28 '11 at 23:12
  • huh let me retry and fix. -0 is definitely a valid option for parallel. Could you maybe have an old version? Not sure. – Phil Hollenback Jan 29 '11 at 00:24
  • Small optimizations: -0/-print0 is only needed if the filename contains \n (space or ' or " are not a problem). -j +0 has been default since 20110122. {} is added if {} is not in the command. Only special chars needs to be quoted. So this should be equivalent: find /my/folder -name '*jpg' | parallel --progress mogrify -resize 800"\>" See GNU Parallel if you are at FOSDEM http://fosdem.org/2011/schedule/event/parallel – Ole Tange Jan 29 '11 at 15:16
  • 1
    Thanks Ole for the tips. I'm still learning parallel obviously. – Phil Hollenback Jan 29 '11 at 18:10
  • Thanks for updating this. But I still do not get parallel to work here. Does it work for you guys in this case? I get: parallel: invalid option -- '-'parallel [OPTIONS] command -- arguments for each argument, run command with argument, in parallel for both Phil's and Ole's commands –  Jan 30 '11 at 04:15
  • Check your version of parallel, I suspect you may have an old one. Mine says GNU parallel 20100922. – Phil Hollenback Jan 30 '11 at 05:49
2

Edit: Sorry, I just realized that's not redirection in your command. It's a special instruction to mogrify.

I would omit the single quotes and try this:

find /my/folder/ -name '*jpg' -exec mogrify -resize 900">" {} \;

Using the command as you posted it, you're probably getting error messages like:

find: `mogrify -resize 900">"filename': No such file or directory.

Original Answer:

To make redirection work within -exec you need to use sh -c:

find /my/folder/ -name '*jpg' -exec sh -c 'mogrify -resize 900 > "$@" ' _ {} \;

It's only been in the last few days that I've started seeing people question the use of -exec. I haven't seen anyone say specifically what's wrong with it. I have a lot of experience with using it and it's never failed me. However, there are times that xargs is definitely advantageous because of the control it provides and its ability to pass multiple arguments at the same time. However, when you use -I, it only passes one argument at a time. The command I've shown here does have the disadvantage of invoking the shell for each found file.

Dennis Williamson
  • 62,149
  • 16
  • 116
  • 151
0

Its old here but no one said anything about a simple:

ls | xargs mogrify -resize 900

that will deal with all files in current directory -good for a thunar custom action too, add -quality xx as well.

Or for custom action on only selected files:

mogrify -resize 900 %F

HopelessN00b
  • 53,795
  • 33
  • 135
  • 209
dav
  • 1