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.

1

3 Answers 3

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.

1
  • 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, 2016 at 0: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' \;
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.

3
  • @don_crissti There is a second meaning to -format but I misunderstood it. There is also -set but I don't get its syntax: imagemagick.org/Usage/files/#save_escapes I don't think I will invest any more time into this.
    – phk
    Dec 26, 2016 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, 2016 at 22:02
  • @phk Your snipped worked too, however I don't know which answer is the better/more efficient one.
    – LucaTony
    Dec 26, 2016 at 22:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .