0

I'm trying to create a little script that will take file names then sort that list by size and doing command over that list. So far I have:

#!/bin/bash
filenames="one \
           two \
           three"
for i in $filenames; do
...
done

It works but I would like to sort the list by filesize before it hits the for command.

1

2 Answers 2

2

If your filenames don't contain any newlines and your system has GNU stat, you could:

  • Use an array variable filenames
  • Use stat to print the size and filename of each file, then use sort on the result (reverse numeric) and then remove the first field (the size) with cut to get the filenames ordered by size (largest first).
  • Feed the result to a while loop for further processing.

filenames=( one two three )
while IFS= read -r file; do
  echo "do something with $file"
done < <(stat --printf '%s\t%n\n' -- "${filenames[@]}" | sort -nr | cut -f2-)

The same as above, but using the null character as delimiter (if you can't guarantee your filenames won't contain newlines; you'll also need GNU sort and GNU cut):

filenames=( one two three )
while IFS= read -r -d $'\0' file; do
  printf 'do something with %s\n' "$file"
done < <(stat --printf '%s\t%n\0' -- "${filenames[@]}" | sort -znr | cut -zf2-)

Note that for filenames of type symlink, it's the size of the link itself (as opposed to that of the file it points to) that is considered. Add the -L option to stat to change that.

Note that it won't work for a file called - as stat interprets it as meaning the file open on stdin. Use ./- instead to work around it.

2
  • That works perfect, thanks! Can I ask a couple of questions though, is < < at the end done just saying to the loop to expect input from the following? I've never seen that before. Would changing the filename variable to a read break anything so I can just pipe the new filenames to it instead of changing that variable Everytime?
    – rathel
    Jun 16, 2020 at 18:18
  • Yes, < is a input redirection and <(...) is a process substitution and behaves as if the list of commands inside it were a file. You can replace "${filenames[@]}" with a list of your filenames if you don't want to use an array.
    – Freddy
    Jun 16, 2020 at 18:41
1

With zsh, same method as in that answer to a similar question about sorting by mtime, itself adapted from that one, to sort arrays based on arbitrary orders (see that latter one for details):

zmodload zsh/stat

array=(one two three...)

# store sizes in an "A"ssociative array:
typeset -A size
stat -nL -A size +size -- $array

# sort using the oe glob qualifier "n"umerically
sorted_array=(/(e['reply=($array)']noe['REPLY=$size[$REPLY]'])

You must log in to answer this question.

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