290

How do you sort du -sh /dir/* by size? I read one site that said use | sort -n but that's obviously not right. Here's an example that is wrong.

[~]# du -sh /var/* | sort -n
0       /var/mail
1.2M    /var/www
1.8M    /var/tmp
1.9G    /var/named
2.9M    /var/run
4.1G    /var/log
8.0K    /var/account
8.0K    /var/crash
8.0K    /var/cvs
8.0K    /var/games
8.0K    /var/local
8.0K    /var/nis
8.0K    /var/opt
8.0K    /var/preserve
8.0K    /var/racoon
12K     /var/aquota.user
12K     /var/portsentry
16K     /var/ftp
16K     /var/quota.user
20K     /var/yp
24K     /var/db
28K     /var/empty
32K     /var/lock
84K     /var/profiles
224M    /var/netenberg
235M    /var/cpanel
245M    /var/cache
620M    /var/lib
748K    /var/spool
3

18 Answers 18

395

If you have GNU coreutils (common in most Linux distributions), you can use

du -sh -- * | sort -h

The -h option tells sort that the input is the human-readable format (number with unit; 1024-based so that 1023 is considered less than 1K which happens to match what GNU du -h does).

This feature was added to GNU Core Utilities 7.5 in Aug 2009.

Note:

If you are using an older version of Mac OSX, you need to install coreutils with brew install coreutils, then use gsort as drop-in replacement of sort.

Newer versions of macOS (verified on Mojave) support sort -h natively.

4
  • 47
    note: add -r to sort, if you want the big ones at the top Commented Dec 9, 2010 at 12:52
  • 9
    On OSX you can install coreutils via brew and add the bin folder to your PATH into your rc file, and -h should be available.
    – kenorb
    Commented Mar 5, 2015 at 14:20
  • Oh - thank you for the -r reminder. that means I don't need the tail command to see the hogs.
    – SDsolar
    Commented Aug 17, 2017 at 8:26
  • A variant of this is easier to edit on the terminal: sort -hr <(du -sh /absolute/path/*). For anyone zooming on a directory with a full disk, it also reverses the order.
    – cbugk
    Commented Oct 5, 2023 at 13:13
56

Try using the -k flag to count 1K blocks intead of using human-readable. Then, you have a common unit and can easily do a numeric sort.

du -ck | sort -n

You don't explictly require human units, but if you did, then there are a bunch of ways to do it. Many seem to use the 1K block technique above, and then make a second call to du.

https://serverfault.com/questions/62411/how-can-i-sort-du-h-output-by-size

If you want to see the KB units added, use:

du -k | sed -e 's_^\([0-9]*\)_\1 KB_' | sort -n
1
  • 3
    nice not to have to install something else to get the results I need
    – taranaki
    Commented Mar 3, 2017 at 19:07
19

If you don't have a recent version of GNU coreutils, you can call du without -h to get sortable output, and produce human-friendly output with a little postprocessing. This has the advantage of working even if your version of du doesn't have the -h flag.

du -k | sort -n | awk '
    function human(x) {
        if (x<1000) {return x} else {x/=1024}
        s="kMGTEPZY";
        while (x>=1000 && length(s)>1)
            {x/=1024; s=substr(s,2)}
        return int(x+0.5) substr(s,1,1)
    }
    {gsub(/^[0-9]+/, human($1)); print}'

If you want SI suffixes (i.e. multiples of 1000 rather than 1024), change 1024 to 1000 in the while loop body. (Note that that 1000 in the condition is intended, so that you get e.g. 1M rather than 1000k.)

If your du has an option to display sizes in bytes (e.g. -b or -B 1 — note that this may have the side effect of counting actual file sizes rather than disk usage), add a space to the beginning of s (i.e. s=" kMGTEPYZ";), or add if (x<1000) {return x} else {x/=1024} at the beginning of the human function.

Displaying a decimal digit for numbers in the range 1–10 is left as an exercise to the reader.

1
  • This is has been the one out of the box solution I've found to work on both linux and mac. Thanks very much!
    – Nahydrin
    Commented Aug 1, 2016 at 21:46
10

If you don't have sort -h you can do this:

du -sh * | sed 's/\([[:digit:]]\)\t/\1B\t/' | sed 's/\(.\t\)/\t\1/' | sed 's/G\t/Z\t/' | sort -n -k 2d,2 -k 1n,1 | sed 's/Z\t/G\t/'

This gets the du list, separates the suffix, and sorts using that. Since there is no suffix for <1K, the first sed adds a B (for byte). The second sed adds a delimiter between the digit and the suffix. The third sed converts G to Z so that it's bigger than M; if you have terabyte files, you'll have to convert G to Y and T to Z. Finally, we sort by the two columns, then we replace the G suffix.

1
  • Awesome effort, but this doesn't come close to working for me.
    – jvriesem
    Commented Sep 3, 2015 at 2:55
7

On OS X, you can install the needed coreutils via Homebrew:

brew install coreutils

With this you'll have gsort, which includes the -h command line parameter.

5

Since Mac OS X doesn't have the -h option for sort (I was probably using Mavericks or Yosemite), so I tried and learned sed and awk for a first attempt:

du -sk * | sort -g | awk '{ numBytes = $1 * 1024; numUnits = split("B K M G T P", unit); num = numBytes; iUnit = 0; while(num >= 1024 && iUnit + 1 < numUnits) { num = num / 1024; iUnit++; } $1 = sprintf( ((num == 0) ? "%6d%s " : "%6.1f%s "), num, unit[iUnit + 1]); print $0; }'

it is a long line. Expanded, it is:

du -sk * | sort -g | awk '{ 

    numBytes = $1 * 1024; 
    numUnits = split("B K M G T P", unit); 
    num = numBytes; 
    iUnit = 0; 

    while(num >= 1024 && iUnit + 1 < numUnits) { 
        num = num / 1024; 
        iUnit++; 
    } 

    $1 = sprintf( ((num == 0) ? "%6d%s " : "%6.1f%s "), num, unit[iUnit + 1]);
    print $0; 

}'

I tried it on Mac OS X Mavericks, Yosemite, Ubuntu 2014-04 with awk being the default awk (which is nawk, because both awk and nawk point to /usr/bin/mawk) or gawk, and they all worked.

Here is a sample of the output on a Mac:

     0B  bar
     0B  foo
   4.0K  wah
  43.0M  Documents
   1.2G  Music
   2.5G  Desktop
   4.7G  Movies
   5.6G  VirtualBox VMs
   9.0G  Dropbox
  11.7G  Library
  21.2G  Pictures
  27.0G  Downloads

instead of du -sk *, I saw in @Stefan's answer where the grand total is also displayed, and without traversing any filesystem mount point, by using du -skcx *

4

This little Perl script does the trick. Save it as duh (or whatever you want) and call it with duh /dir/*

#!/usr/bin/perl -w
use strict;

my @line;

sub to_human_readable {
        my ($number) = @_;
        my @postfix = qw( k M G T P );
        my $post;
        my $divide = 1;
        foreach (@postfix) {
                $post = $_;
                last if (($number / ($divide * 1024)) < 1);
                $divide = $divide * 1024;
        }
        $number = int($number/$divide + 0.5);
        return $number . $post;
}

sub trimlengthright {
        my ($txt, $len) = @_;
        if ( length($txt) >= $len ) {
                $txt = substr($txt,0,$len - 1) . " ";
        } else {
                $txt = $txt . " " x ($len - length($txt));
        }
        return $txt;
}

sub trimlengthleft {
        my ($txt, $len) = @_;
        if ( length($txt) >= $len ) {
                $txt = substr($txt,0,$len - 1) . " ";
        } else {
                $txt = " " x ($len - length($txt)) . $txt;
        }
        return $txt;
}

open(DF,"du -ks @ARGV | sort -n |");
while (<DF>) {
        @line = split;
        print &trimlengthleft(&to_human_readable($line[0]),5)," "; # size
        print &trimlengthright($line[1],70),"\n"; # directory
}
close DF;
3

Command:

du -ah . | sort -k1 -h | tail -n 50

Explanation:

  • List size of all files/folders recursively in the current directory in human-readable form

du -ah .

  • Sort the human-readable size which is present in the first column and keep the largest 50

sort -k1 -h | tail -n 50

1

Here's what I use on Ubuntu 10.04, CentOS 5.5, FreeBSD and Mac OS X.

I borrowed the idea from www.geekology.co.za/ and earthinfo.org, as well as the infamous ducks from "Linux Server Hacks" by O'Reilly. I am still adapting it to my needs. This is still a work in progress (As in, I was working on this on the train this morning.):

#! /usr/bin/env bash
ducks () {
    du -cks -x | sort -n | while read size fname; do
        for unit in k M G T P E Z Y; do
            if [ $size -lt 1024 ]; then
                echo -e "${size}${unit}\t${fname}"
                break
            fi
            size=$((size/1024))
        done
    done
}
ducks > .ducks && tail .ducks

Here's the output:

stefan@darwin:~ $ ducks
32M src
42M .cpan
43M .macports
754M    doc
865M    Work
1G  .Trash
4G  Library
17G Downloads
30G Documents
56G total

stefan@darwin:~ $
3
  • I think you meant du -cks -x * ? (with the asterisk) Commented Apr 22, 2015 at 9:02
  • The asterisk is redundant in this usage. Give it a try. Commented Apr 22, 2015 at 18:39
  • do you mean putting the first set of code into a file called ducks, and then chmod a+x ducks and then use ./ducks to run it? Then I only see the total disk usage, on both Mac OS X and on Ubuntu 2014-10. I also tried putting the ducks() { ...} definition into .bashrc and then use ducks to run it, and the same thing on Mac OS X, only see the grand total Commented Apr 23, 2015 at 7:20
1

Go crazy with this script -

$du -k ./* | 
> sort -nr |
> awk '
> {split("KB,MB,GB",size,",");}
> {x = 1;while ($1 >= 1024) {$1 = $1 / 1024;x = x + 1} $1 = sprintf("%-4.2f%s", $1, size[x]); print $0;}'
1

In the absence of GNU sort -h, this should work in most UNIX environments:

join -1 2 -2 2 <(du -sk /dir/* 2>/dev/null | sort -k2,2) <(du -sh /dir/* 2>/dev/null | sort -k2,2) | sort -nk2,2 | awk '{ print $3 "\t" $1 }'
0

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
0

This will sort the output in decreasing order of size:

du -sh /var/* | sort -k 1rn

This will sort the output in increasing order of size:

du -sh /var/* | sort -k 1n

PS : this can be used to sort by any column but that column values should be in same format

1
  • 1
    No. sort -k1rn is equivalent to sort -rn and just sorts numerically based on the initial sequence of decimal digits on each line. It doesn't understand floating point, and it doesn't understand the k, M, G... suffixes. 10.1k would be considered greater than 1.23G Commented Jan 17, 2017 at 14:52
0

Tested on Solaris!

du -kh | sort -nk1 | grep [0-9]K && du -kh | sort -nk1 | grep [0-9]M && du -kh | sort -nk1 | grep [0-9]G

This will output all directory sizes recursively, at the bottom will be largest directory in Gigabytes and at the top smallest in Kilobytes.

0

The biggest is at the bottom:

du -sh * | sort -h
-1

To sort by size in MB

du --block-size=MiB --max-depth=1 path | sort -n
1
  • The user wants to get the output of du -h (human readable output) sorted numerically. You're not providing an answer to that. You may also want to link your UNIX-SE account with the other accounts you have on the other SE sites.
    – Lætitia
    Commented Mar 11, 2013 at 11:58
-2

This script is even easier:

for i in G M K; do du -h -d1 / | grep [0-9]$i | sort -n; done
0
-2

for OSX

du -h -k  {PATH} | sort -n
1
  • isn't the -k just cancelling -h and if so how does this provide the human readable output requested by the OP.
    – Anthon
    Commented Oct 10, 2015 at 7:58

You must log in to answer this question.

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