Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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.

Thanks!

share|improve this question
add comment

3 Answers

up vote 6 down vote accepted

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
  ((++i))
done
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
    ((++i))
    ln -s $x ${i#1}.png
done

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);
    }
'
share|improve this answer
    
Friggin awesome and comprehensive answer - thanks! –  radven Jan 2 '11 at 17:47
add comment

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.

share|improve this answer
add comment

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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