I have a large number (more than 400) of directories full of photos. What I want to do is to keep the larger sizes of these photos. Each directory has 31 to 66 files in it.

Each directory has thumbnails, and larger versions, plus a file called example.jpg

I dispatched the example.jpg file easily with:

rm */example.jpg

I initially thought that it would be easy to delete the thumbnails, but the problem is they are not consistently named. The typical pattern was photo1.jpg and photo1s.jpg. I did rm */photo*s.jpg but it ended up some of the files named photoXs.jpg were actually larger and not smaller. Argh.

So what I want to do is scan each directory for filesize and delete (or move) the thumbnails. I initially thought I'd just ls -R every file and extract the size of each file and save those under a threshold. The problem? In one directory the large will be 1.1 MB and the thumb is 200k. In another the large is 200k and the small 30k.

Even worse, the files really are mostly named photo1.jpg - so simply putting them all in the same folder, sorting by size, and deleting in groups would not work without renaming already, and if it's possible I'd prefer to keep them in their folders.

I was almost resolved to just doing this all manually, but then thought I'd ask here.

How would you do this task?

  • Is there any relationship between the name of an image and it's thumbnail image or other sized images? – Marnix A. van Ammers Apr 29 '10 at 15:59

This script makes the assumption that there's a significant gap in the sizes between the groups of smallest and largest files. In particular that the smallest of the large files is at least twice as big as the largest of the small files.

Call the script "imagedirstats" and run it in a loop such as this one:

find /path/to/main/branch -type d | while read subdir; do (cd "$subdir" && ~/bin/imagedirstats ); done

in order to find and delete files in indivdual directories in tree of directories.

Here is the script:

# from http://superuser.com/questions/135951/batch-deletion-of-smaller-files-from-group-of-files-via-unix-command-line
# by Dennis Williamson - 2010-04-29

prevn=1     # prevent division by zero
factor=4    # how close to the largest of the small files to set the threshold, 4 == one fourth of the way above
min=1000    # ignore files below this size

while read n
    (( ratio = n / prevn ))
    if (( ratio > 1 && n > min ))
    if (( n > 0 ))
done < <(find . -maxdepth 1 -name "*.jpg" -printf "%s\n" | sort -n)
# for OS X, comment out the preceding line and uncomment this one:
# done < <(find . -maxdepth 1 -name "*.jpg" | stat -f "%z" | sort -n)

# the following line would be the GNU equivalent using stat(1) instead of printf
# it's included here for reference:
# done < <(find . -maxdepth 1 -name "*.jpg" | stat -c "%s" | sort -n)

(( size = (n - prevn) / factor + prevn ))

echo "Smallest of the large: $n"
echo "Largest of the small: $prevn"
echo "Ratio: $ratio"
echo "Threshold: $size"

if (( ratio < 2 ))
    read -p "Warning: ratio too small. Delete anyway? Only 'Yes' will proceed" reply
    if [[ $reply != "Yes" ]]
        echo "Cancelled" >&2
        exit 1

# uncomment the delete on the following line to actually do the deletion

find . -maxdepth 1 -name "*.jpg" -size -${size}c # -delete

Edit: Moved the warning prompt so useful information would be displayed first. Fixed a missing fi.

Edit 2: Made the two find commands consistent. Added a commented-out variation for OS X. Added information on running the script.

  • Saved this as imagedirstats: and ran it with ~/bin/imagedirstats */* and the result is: -bash: /Users/artlung/bin/imagedirstats: Argument list too long ... run with ~/bin/imagedirstats * and I get: find: illegal option -- p find: illegal option -- r find: illegal option -- i find: illegal option -- n find: illegal option -- t find: %s\n: No such file or directory /Users/artlung/bin/imagedirstats: line 38: syntax error: unexpected end of file – artlung Apr 29 '10 at 15:37
  • The script doesn't take any arguments. It's designed to be run in the current directory or inside a loop that iterates over a set of directories. Also, see where I edited the find command. I forgot to include a -maxdepth argument. – Paused until further notice. Apr 29 '10 at 16:02
  • What does find --version give you? What Linux distribution (and version)? – Paused until further notice. Apr 29 '10 at 16:08
  • The reason for the current-directory and maxdepth restrictions is to accommodate the condition you described in your question in which file size ranges overlap across directories. – Paused until further notice. Apr 29 '10 at 16:11
  • This is what the outer loop might look like: find /path/to/main/branch -type d | while read subdir; do (cd "$subdir" && ~/bin/imagedirstats ); done – Paused until further notice. Apr 29 '10 at 16:16

If there's some specific cuttoff you can find, for example all large pictures are larger than 200KB, then you can do this:

find */*.jpg -size -200k -delete

You might want to make a backup copy first.

  • This is promising. There's not a clear cutoff in size, unfortunately, but this is a nice way to narrow down a majority maybe. Thanks! – artlung Apr 29 '10 at 14:41

If the file sizes aren't consistent, are the image dimensions?

You could use identify, a tool that comes with ImageMagick, to grab the image dimensions. Through some simple bash scripting, you can work with the images depending on their size.

To grab the width and height of an image with identify:

identify -format '%wx%h' filename

you'll get output like so:

[john@awesome:~]$ identify -format '%wx%h' W4.JPG

You can then use the cut utility to grab the numbers in your script:

[john@awesome:~]$ identify -format '%wx%h' W4.JPG | cut -d'x' -f1
[john@awesome:~]$ identify -format '%wx%h' W4.JPG | cut -d'x' -f2
  • Okay, installing imagemagick via brew (on Mac) and will take a look. this is a piece for extracting height. I think there's some consistency in the heights of the thumbnails -- not in the larger ones. Promising! – artlung Apr 29 '10 at 14:40
  • Even if they aren't exactly consistent, if all thumbnails are less than 200 pixels in height you can do if [ $h -lt 200 ] ... – John T Apr 29 '10 at 15:02

If you want to do this on the basis of file name, try the following:

find -name '*.jpg' | sed -ne 's:^\(.*\)\.jpg:\1s.jpg$:p' | xargs rm

It will find every .jpg file, put an "s" at the end of the filename (just before the ".") and delete it.

  • 1
    The OP said the files are inconsistently named and some of the large files have "s". – Paused until further notice. Apr 29 '10 at 14:54
  • Thanks petersohn, but DW is right. If they were named like a sane person would name them then it would be much easier to simply delete files matching the pattern and be done. – artlung Apr 29 '10 at 14:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.