I've got a deep directory tree containing .PNG files. I'd like to find all the .PNG files in the directory, sort them in order of size from smallest to largest, and then display every 50th image.

(I'm analyzing data and trying to find the best size cutoff between "potentially useful" and "random noise" so I want an easy way to skim through these thousands of images....)

Any scripting help is appreciated. I know how to use 'find' to search by size, but not how to sort the results or run a display program to display every 50th without having it pause the process waiting.

I am using MacOS Snow Leopard, btw.



Is that size as in file size, or size as in image dimensions?

In zsh, to see all .png files in the current directory and its subdirectories, sorted by increasing file size:

echo **/*.png(oL)

There's no convenient glob qualifier for grabbing every N files. Here's a loop that sets the array $a to contain every 50th file (starting with the largest).

a=() i=0
for x in **/*.png(OL); do
  ((i%50)) || a+=$x
my-favorite-image-viewer $a

Without zsh or GNU find, there's no easy way of sorting find output by metadata (there's find -ls or find -exec ls or find -exec stat, but they might not work with files containing non-printable characters, so I don't like to recommend them). Here's a way to do it in Perl.

find . -name '*.png' |
perl -e '
    $, = "\n";  # separate elements by newlines in the output
    print  # print…
        sort {-s $a <=> -s $b}  # …sorted by file size…
            map {chomp;$_} <>  #…the input lines (with the newline bitten off)

And here's a way to view every 50th file (starting with the largest):

find . -name '*.png' |
perl -e '
    $, = "\n";
    exec "my-favorite-image-viewer",
        map {$i++ % 50 ? () : $_}  # every 50
            sort {-s $b <=> -s $a} map {chomp;$_} <>

Another approach would be to create symbolic links in a single directory, with names ordered by file size. In zsh:

mkdir tmp && cd tmp
i=1000000  # the extra 1 on the left ensures alignment
for x in ../**/*(oL); do
    ln -s $x ${i#1}.png

With Perl:

mkdir tmp && cd tmp
find .. -name '*.png' |
perl -e '
    $, = "\n";
    for $x (sort {-s $a <=> -s $b} map {chomp;$_} <>) {
        symlink $x, sprintf("%06d", ++$i);
  • Friggin awesome and comprehensive answer - thanks! – radven Jan 2 '11 at 17:47

Just combine find to search for *.png, ls or stat to get the file size, sort to sort by file size and awk to print only every 50th line.


If you don't have GNU tools or if your filenames contain lots of special characters, I would use one of Gilles' excellent solutions.

However, here is one solution using GNU find, sort, cut and awk-- basically the set of tools that alex suggested:

$ find -iname "*.png" -exec ls -s {} + | sort -n | cut -d" " --complement -f1 | awk '{ if ( NR % 50 == 0 ) print}' | xargs -d"\n" eog

Here I've used eye-of-gnome (eog) as the image viewer, mostly because it was one of the few I could find that take multiple command line arguments and does something sensible with it.

I'm sure one could remove the sort and the cut in favor of some more awk code. To be completely honest, I'm not sure how this solution will interact with whitespace.

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.