How to easily list all directories and sort them by size on Linux

List All Directories And Sort Them By Size On Linux

If you run ls -lh and wonder why every directory shows 4.0K, you are looking at the directory entry itself, not what lives inside the tree. du reads what is underneath.

Why ls cannot sort directories by size

ls reads directory metadata, so ls -S sorts regular files correctly while directories stay at their inode size of 4.0K.

Use du (disk usage) when you want the sum of everything under a path. It walks the tree, adds block usage, and prints one line per directory, which is exactly what sort needs to rank.

  • Use du when you need combined size, including subdirectories
  • Use ls when you need name, time, or permission sorting without size

Meet du and its sizing flags

I verified these on GNU coreutils 9.4, which is what Ubuntu 24.04, Debian 12, and Fedora 40 ship today. The same flags exist on older 8.x releases, only the help text differs.

du --version
# du (GNU coreutils) 9.4

man du | grep -A2 "max-depth"

The flags you will actually use for a sorted listing are few, so keep them together rather than rediscovering them each time.

FlagWhat it doesWhen you need it
-h, –human-readablePrints K, M, G so you can read the output without mathAlways for interactive use
-s, –summarizeShows only the total for each argument, not every subdirectoryWhen you pass */ or explicit paths
-d N, –max-depth=NLimits recursion to N levels (0 is the argument itself)-d 1 for immediate children only
-a, –allIncludes individual files, not just directoriesTo find the biggest files inside a tree
–apparent-sizeShows logical file size instead of on-disk block usageWhen sparse files or block rounding confuses you
–block-size=1M, -B 1MForces a single unit for stable numeric sortingWhen sort -h is unavailable
-x, –one-file-systemDoes not cross mount pointsOn hosts with /proc, /snap, or network mounts

-d and –max-depth are the same option, so both forms print the same result. I use the short flag when typing and the long flag in scripts because that reads clearly when you revisit the pipeline.

List immediate directories with human sizes

Start with the simplest useful rank. This prints every immediate child of the current directory plus the total for the current directory itself, all in human units.

du -h --max-depth=1 .
du output showing immediate subdirectories with human sizes
Immediate subdirectories listed with human-readable sizes

On my demo tree with 100K, 250K and 500K subdirectories, the output was unsorted disk order: 104K for alpha, 560K for beta, 256K for gamma, and 924K for the parent. The numbers are slightly larger than the raw file sizes because du counts filesystem blocks, which means a 100K file occupies a few extra kilobytes on disk.

# explicit path works the same
du -h --max-depth=1 /var/log

# long form included for clarity in scripts
du -h -d 1 /home/aadesh/projects

Add 2> /dev/null when a tree contains unreadable directories, because otherwise du: cannot read directory warnings interleave with the sizes and break the sort.

Sort the listing so the biggest directory is on top

Pipe to sort -h for ascending or sort -hr for descending human-numeric order.

  • sort -h understands K, M, G and arrived in coreutils 7.5
  • sort -n compares only numbers and breaks on human sizes
du -h --max-depth=1 . | sort -h      # smallest first
du -h --max-depth=1 . | sort -hr     # largest first
LC_ALL=C du -h --max-depth=1 . | sort -hr  # locale-stable when needed
Sorted directories largest first via sort -hr
Directories ranked largest first with sort -hr

I ran the descending variant on the same demo tree and got the expected rank: parent at 924K, then beta at 560K, gamma at 256K, alpha at 104K. Setting LC_ALL=C forces byte-wise sorting when a UTF-8 locale would otherwise treat human suffixes oddly, which matters on some Alpine and busybox-adjacent images.

If you are on a minimal image without sort -h, force a single unit and use numeric sort instead: du –block-size=1M . | sort -nr gives the same ranking at the cost of raw megabytes.

Show only the top N directories

Most of the time you want the biggest few, not the whole list. Combine tail to drop the parent total and head to keep N lines, and keep the sort you already have.

du -h --max-depth=1 2> /dev/null | sort -hr | tail -n +2 | head -n 10
Top directories without parent total via tail and head
Top directories without the parent total

tail -n +2 skips line one, which is the current directory total after a descending sort. head -n 10 then keeps the ten largest children. On the demo tree this left beta, gamma, alpha in that order and removed the 924K parent line entirely.

# without the parent total at all, summarize the children directly
du -sh --apparent-size ./*/ 2>/dev/null | sort -hr | head -n 10

# explicit count matters in scripts; plain head defaults to 10
du -h --max-depth=1 2> /dev/null | sort -hr | tail -n +2 | head

du -sh ./*/ avoids the parent line from the start because -s summarizes each argument. It needs a shell that expands */ and will miss dot-directories, so the du –max-depth form is the safer default when you are unsure.

Handle spaces, dot-directories, and permission noise

A pipeline that breaks on My Photos is not a pipeline you can trust. du prints names verbatim, and sort operates on the first column only, so spaces in names are safe here, but glob and null-byte handling still matters for later steps.

# include dot-directories if you need them
du -h --max-depth=1 . 2>/dev/null | sort -hr

# exclude pseudo-filesystems that inflate totals
du -h --max-depth=1 -x / 2>/dev/null | sort -hr | head

# when a tree is unreadable, silence is required for clean sorting
du -h --max-depth=1 /var/log 2>/dev/null | sort -hr | head

# for scripting without locale surprises
LC_ALL=C du -h -d 1 . 2>/dev/null | sort -hr | head -n 5

Use -x on the root filesystem because du would otherwise descend into /proc and /sys and stall. Use redirection rather than ignoring stderr when the output feeds sort, because warnings are not sizes.

  • Stay on one filesystem with -x on /
  • Redirect permission warnings to /dev/null so sort sees only sizes

Find the largest directories anywhere in a tree

When the biggest consumer is nested two or three levels down, immediate children hide it. Let du list all files and directories, then filter to directories only.

# largest files and directories together
du -ah . 2>/dev/null | sort -hr | head -n 20

# directories only, at any depth
du -h . 2>/dev/null | sort -hr | head -n 20

# alternative: let find enumerate directories, then size each one
find . -maxdepth 3 -type d -exec du -sh {} + 2>/dev/null | sort -hr | head -n 20

# threshold: only show entries over 100M
du -h --threshold=100M . 2>/dev/null | sort -hr | head

On the demo tree, du -ah . | sort -hr | head surfaced the 500K file inside beta before its sibling directories, which helps when a single log or cache file dominates. find -type d is slower but honest about hidden empties that du still reports with 4.0K.

–threshold=SIZE is available since coreutils 8.31 and avoids the grep you would otherwise add to filter small entries. It filters before sorting, which keeps large trees fast.

Make the ranking reusable with an alias or function

Typing the same five-stage pipeline invites a typo. Put a small function in ~/.bashrc or ~/.zshrc so you get tab completion and a count argument.

ArgumentDefaultEffect
dir.Directory to rank
n10How many lines to keep
# put this in ~/.bashrc or ~/.zshrc, then run: source ~/.bashrc
dusort() {
  local dir="${1:-.}"
  local n="${2:-10}"
  du -h --max-depth=1 "$dir" 2>/dev/null | sort -hr | tail -n +2 | head -n "$n"
}

# usage
dusort              # top 10 in .
dusort /var/log     # top 10 in /var/log
dusort /home 20     # top 20 in /home

A shell alias works too, but it cannot take a directory argument cleanly. The function above handles spaces because “$dir” is quoted, and it defaults to the current directory when you call it bare.

# simple alias when you always want the same 10
alias dush='du -h --max-depth=1 2> /dev/null | sort -hr | tail -n +2 | head -n 10'

When du is not the right lens

du counts blocks, so a 1-byte sparse file can report 0, and a file with holes reports less than ls -lh shows. Add –apparent-size when you need logical size parity with ls, and keep the default when you need true disk usage.

# logical size vs disk usage on the same tree
du -sh --apparent-size demo/* | sort -hr
du -sh demo/* | sort -hr
# sparse-file check
du -h --apparent-size sparse.img
du -h sparse.img

For an interactive exploration, ncdu beats any pipeline. It scans once and lets you navigate with arrow keys, delete, and re-scan without rebuilding the sort.

sudo apt update && sudo apt install -y ncdu   # Debian/Ubuntu
sudo dnf install -y ncdu                   # Fedora
ncdu --color dark -x /home                 # stay on one filesystem

Modern replacements like dust and duf add charts and mount-aware views, but they are not preinstalled on servers. Learn the pipeline first, because it exists everywhere you will ssh into.

Quick reference for your terminal

Copy the line that matches your intent. Each one is a complete command, not a fragment, and each was run against GNU coreutils 9.4 for this article.

GoalPipeline
Largest immediate childrendu -h -d 1 | sort -hr
Top 10 without parentdu -h -d 1 | sort -hr | tail -n +2 | head
du -h --max-depth=1 . | sort -hr                              # rank immediate subdirs, largest first
du -h --max-depth=1 2> /dev/null | sort -hr | tail -n +2 | head -n 10  # top 10 without parent total
du -sh ./*/ 2>/dev/null | sort -hr | head -n 10                 # same, via explicit args
du -ah . 2>/dev/null | sort -hr | head -n 20                   # biggest files and dirs anywhere
du -h . 2>/dev/null | sort -hr | head -n 20                    # directories only, any depth
du -h --threshold=100M . 2>/dev/null | sort -hr | head          # only entries over 100M
LC_ALL=C du -h -d 1 . 2>/dev/null | sort -hr | head -n 5     # locale-stable pipeline

Summary

du -h –max-depth=1 lists immediate directories with human sizes, and sort -hr turns that list into a rank where the biggest entry is on top. Add tail -n +2 | head to keep the top N without the parent total, and wrap the whole pipeline in a small shell function so you can run dusort /var/log 20 whenever disk pressure returns.

Why does ls -lh show every directory as 4.0K?

ls reads the directory inode, not the files inside it. Use du -h –max-depth=1 to sum the contents, then pipe to sort -hr to rank by size.

What is the difference between du -h –max-depth=1 and du -sh */?

Both show immediate children, but –max-depth=1 walks from one argument and includes hidden children when combined with .* handling, while du -sh */ expands via the shell and misses dot-directories. –max-depth is the safer default for scripts.

How do I list only the ten largest directories?

Run du -h –max-depth=1 2> /dev/null | sort -hr | tail -n +2 | head -n 10. tail -n +2 drops the parent total that sorts to the top, and head keeps the next ten.

Why does du show a different size than ls?

du counts allocated blocks by default, while ls shows logical file length. Add –apparent-size to du when you want parity with ls. The default block count is what actually occupies disk.