20

How can I sort a list using a human-readable file-size sort, numerical sort that takes size identifier (G,M,K) into account? Can I sort "du -sh" output for example?

Problem: Consider the problem of listing files/folders and sorting them by their size. You can achieve that by running:

du -s * | sort -n

This lists the files/folders sorted by their sizes. However the printed size value is in bytes (or megabytes, or gigabytes if you choose).

It would be desirable to be able to sort based on the human-readable values, so I can run something analogous to

du -sh * | <human-readable file sort>

And have 1.5GB folder shows up after 2.0M.

1

8 Answers 8

4

Afaik, there's no standard command to do this.

There are various workarounds, which were discussed when the same question was asked over at Stack Overflow: How can I sort du -h output by size

38

Use GNU coreutils >= 7.5:

du -hs * | sort -h

(Taken from this serverfault question)

Man page

Edit: You can check your versions using du --version and sort --version if you are using the GNU versions. If you're using homebrew you may need to use gdu and gsort.

2
  • 8
    OSX doesn't have this option. You can use homebrew to brew install coreutils (which prepends all the coreutils commands with a 'g'). You can then do gdu -hs * | gsort -h.
    – dsummersl
    Aug 7, 2014 at 22:17
  • 1
    Just to clarify @dsummersl's point: the du -hs * works fine on Mac OS X, but sort -h returns sort: invalid option -- h. One can also install the coreutils package via MacPorts as described here.
    – jvriesem
    Oct 13, 2014 at 20:43
3

If you are just worried about files larger than 1MB, as it seems you are, you can use this command to sort them and use awk to convert the size to MB:

du -s * | sort -n | awk '{print int($1 / 1024)"M\t"$2}'

Again, this rounds the sizes to the nearest MB. You can modify it converting to the unit of your choice.

2
  • This is similar to: du -sm * | sort -n. -s/-g makes du output sizes in megabytes/gigabytes.
    – notnoop
    Sep 4, 2009 at 8:35
  • For MB you have to divide by 1024 more. So it will be int($1 / (1024 * 1024)) Jul 30, 2014 at 23:33
2

Here's another one:

$ du -B1 | sort -nr | perl -MNumber::Bytes::Human=format_bytes -F'\t' -lane 'print format_bytes($F[0])."\t".$F[1]'

You might have to do a

$ cpan Number::Bytes::Human

first.

2

This one handles filenames with whitespace or apostrophes, and works on systems which do not support xargs -d or sort -h:

du -s * | sort -n | cut -f2 | tr '\n' '\0' | xargs -0 -I {} du -sh "{}"

which results in:

368K    diskmanagementd
392K    racoon
468K    coreaudiod
472K    securityd
660K    sshd
3.6M    php-fpm
1

du -sk * | sort -n | awk '{ print $2 }' | while read f ; do du -sh "$f" ; done

1

This command will sort by size in MB

du --block-size=MiB --max-depth=1 path | sort -n
1
  • That is already what the user is doing actually, he/she just didn't gave the example with MiB but mentioned about it. What he/she is looking for is to be able to sort when using the -h flag to du.
    – Lætitia
    Mar 11, 2013 at 11:41
0

I ended up here since I was trying to sort something else that combined MB and GB in the same output and I couldn't control it.

$NF is used since the #GB or #MB pattern was the last column in the output:

somecommand | \
gawk '{
  if ($NF ~ /[0-9\.]+GB/) 
    { a=gensub(/([0-9\.]+)(GB)/,"\\1","g",$NF); \
    printf "%sMB\n", a*1024} \
  else {print $NF} 
}' | \
sort -n

Explanation of the awk command:

if ($NF ~ /[0-9\.]+GB/)

if the last column matches the regex pattern which contains a digit or a . one or more times followed by GB

{ a=gensub(/([0-9\.]+)(GB)/,"\\1","g",$NF); \

then set variable a to the digit portion of each line which matches the same regex pattern in the same last column ($NF)

printf "%sMB\n", a*1024} \

after setting a, use printf to format the output as ${a*1024}MB

else {print $NF}

otherwise just print the last column

sort -n

use numeric sort on the output


example

echo "4MB\n5GB\n420MB\n420GB\n1024MB\n1GB" | \                                                                                                                                 23:43:06 (EMP-2653/package-upgrades) Ø M
gawk '{
  if ($NF ~ /[0-9\.]+GB/)
    { a=gensub(/([0-9\.]+)(GB)/,"\\1","g",$NF); \
    printf "%sMB\n", a*1024} \
  else {print $NF}
}' | \
sort -n

I'm sure there's a way to reuse the regex pattern so I'm only performing the match once and replacing in place, but I don't know how to do that yet :)

You must log in to answer this question.

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