8

I want a bash script which does the following:

  • Find pictures (jpg,jpeg,JPG,JPEG) recursively from current directory downwards
  • Generate a thumbnail with imagemagick's convert
  • Move thumbnail to other directory

My current script looks like this:

for f in `find . -type f -iname "*.jpg"`
  do
  convert ./"$f" -resize 800x800\> ./"${f%.jpg}_thumb.jpg"
  mv ./"${f%.jpg}_thumb.jpg" /home/user/thumbs/
done

It doesn't convert files (or folders with all content) which have spaces/special characters. I tried with print0 but it didn't help.

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
LucaTony
  • 83
  • 4

3 Answers3

8

Don't iterate over the output of find. The problem you are experiencing is a typical consequence of that.

Your example is a bit tricky due to the file renaming. One not very efficient but safe way to do it is with the -exec option of find, and an additional sh per each file, like this:

find . -type f -iname "*.jpg" -exec sh -c 'echo convert "$1" -resize 800x800\> /home/user/thumbs/"${1%.jpg}_thumb.jpg"' -- {} \;

If you didn't mind using the same name (with .jpg suffix instead of _thumb.jpg), then this simple form would work, and be much more efficient:

find . -type f -iname "*.jpg" -exec echo convert "{}" -resize 800x800\> /home/user/thumbs/"{}" \;

I added echo statements there to check the output before executing the commands. Remove them if the output looks good.

don_crissti
  • 79,330
  • 30
  • 216
  • 245
janos
  • 11,171
  • 3
  • 35
  • 53
  • 2
    @janos, phk and don_crissti: I just accepted the answer from don, but I also upvoted the answers which are working. Thanks for your support guys, you had very interesting/educational answers to my first question on this site :) – LucaTony Dec 27 '16 at 00:06
6

You could use more advanced options like -set combined with percent escapes (namely %t to extract the filename without directory or extension) to do the resize, rename and move of each file with a single convert invocation:

find . -type f -iname \*.jpg -exec convert {} -resize 800x800\> \
-set filename:name '%t' '/home/user/thumbs/%[filename:name]_thumb.jpg' \;
don_crissti
  • 79,330
  • 30
  • 216
  • 245
2

In your solution the file names got split on the default $IFS which includes spaces.

Try the following:

while IFS= read -rd '' f; do

    convert ./"$f" -resize 800x800\> ./"${f%.jpg}_thumb.jpg"
    mv ./"${f%.jpg}_thumb.jpg" /home/user/thumbs/

done < <(find . -type f -iname "*.jpg" -print0)

The find prints the file names separated by null bytes (\0) and using -d '' you set the delimiter of read to the same.

phk
  • 5,893
  • 7
  • 41
  • 70
  • @don_crissti There is a second meaning to `-format` but I misunderstood it. There is also `-set` but I don't get its syntax: http://www.imagemagick.org/Usage/files/#save_escapes I don't think I will invest any more time into this. – phk Dec 26 '16 at 21:58
  • @don_crissti I would feed it all the files as parameters through `find […] -exec mogrify […] {} +` but apparently it's a bad idea for another reason: _"However all three images [in the example] are read into memory, and then modified by the one command. This is not a recommended solution for a number of large images, or very large numbers of images, due to the posibilty [sic] of reaching memory limits and thus going to disk swapping (thrashing)."_ (from the link posted in my previous comment). Maybe the possibility of such cases is also smth. worth mentioning in your long overview post. – phk Dec 26 '16 at 22:02
  • @phk Your snipped worked too, however I don't know which answer is the better/more efficient one. – LucaTony Dec 26 '16 at 22:51