Sort Command in Linux: Complete Guide to Sorting Data, Fields, and Parameters

Sort command and its perimeter

Sort command and its perimeter

I once spent a genuinely embarrassing amount of time confused about why a list of server hostnames like web1, web2, web10 kept coming out in the order web1, web10, web2 no matter what I tried. That’s sort doing exactly what it’s told — plain lexicographic (string) ordering, not numeric — and the fix (sort -V or sort -n depending on context) was a five-second change once I understood what was actually happening. sort looks trivial on the surface, but its field-based, key-based, and type-aware sorting options make it a genuinely deep tool once you need more than “put these lines in order.”

What sort Does

sort reads lines of text (from files or standard input) and writes them back out in sorted order, according to whatever comparison rule you specify. By default, that rule is a straightforward string comparison based on the current locale’s collation order.

$ printf 'banana\napple\ncherry\n' > fruits.txt
$ sort fruits.txt
apple
banana
cherry

Basic Syntax

sort [options] [file...]

If no file is given, sort reads from standard input, making it a natural pipeline component.

Default Sort Behavior and Case

By default, sort orders lines according to locale collation rules, and case matters — in the standard “C”/POSIX collation, uppercase letters sort before lowercase ones:

$ printf 'apple\nbanana\nApple\nAPPLE\ncherry\navocado\n' | sort
APPLE
Apple
apple
avocado
banana
cherry

If you want case-insensitive sorting, -f (fold case) treats uppercase and lowercase as equivalent for comparison purposes:

$ printf 'apple\nbanana\nApple\nAPPLE\ncherry\navocado\n' | sort -f
APPLE
Apple
apple
avocado
banana
cherry

Interestingly, the output order is identical here because folding just affects comparison, not the original casing of the lines themselves — I verified this directly and it’s worth knowing so you’re not surprised when -f doesn’t visibly change anything for already-alphabetically-consistent input.

Reverse Order: -r

$ printf 'apple\nbanana\ncherry\n' | sort -r
cherry
banana
apple

Numeric Sort: -n

This is the fix for the “web1, web10, web2” problem in a plain numeric context — default sort treats input as text, so 10 sorts before 2 because '1' is lexicographically less than '2':

$ printf '10\n2\n33\n4\n' | sort
10
2
33
4
$ printf '10\n2\n33\n4\n' | sort -n
2
4
10
33

Human-Readable Numeric Sort: -h

For sorting values with size suffixes (K, M, G) the way a human expects, rather than as plain strings:

$ printf '2K\n1M\n500\n10K\n' | sort -h
500
2K
10K
1M

This is exactly what I reach for when sorting du -h output by size — sort -h correctly understands that 1M is larger than 10K, which plain -n cannot do since it doesn’t parse unit suffixes at all.

$ du -h /var/log/* | sort -h

Version Sort: -V

This is the actual fix for the hostname problem from my intro — -V performs “natural” sorting that understands embedded numbers as numbers, exactly the way version numbers or numbered filenames are meant to be compared:

$ printf 'file10.txt\nfile2.txt\nfile1.txt\n' | sort -V
file1.txt
file2.txt
file10.txt

-V is specifically designed for exactly this case — filenames and version strings with embedded numeric components — and it’s the option I now reach for by default whenever I’m sorting anything that looks like name<number>.extension.

Month Sort: -M

$ printf 'Mar\nJan\nDec\nFeb\n' | sort -M
Jan
Feb
Mar
Dec

-M recognizes three-letter month abbreviations and sorts them chronologically rather than alphabetically — genuinely useful when processing log output that includes month names, like classic syslog timestamp formats.

Removing Duplicates: -u

$ printf '3\n1\n2\n1\n3\n' | sort -u
1
2
3

-u sorts and deduplicates in a single pass — functionally similar to sort | uniq but faster since it’s done as one operation rather than two separate processes.

Random Order: -R

$ printf '1\n2\n3\n' | sort -R
3
2
1

-R randomly shuffles the input rather than sorting it in any meaningful order — I’ve used this for picking a random sample from a list, like selecting a random subset of servers for a canary deployment: sort -R hostnames.txt | head -5.

Checking Whether Input Is Already Sorted: -c

$ printf '1\n2\n3\n' | sort -c; echo "exit:$?"
exit:0

$ printf '3\n1\n2\n' | sort -c; echo "exit:$?"
sort: -:2: disorder: 1
exit:1

-c doesn’t sort anything — it verifies whether the input is already sorted and reports the first out-of-order line if not, exiting non-zero. I use this in data-validation scripts before running downstream tools (like join or comm) that require pre-sorted input as a precondition.

Sorting by Field/Key: -k

This is the option that unlocks sort‘s real power for structured data — sorting by a specific column rather than the whole line.

$ printf 'bob 30\nalice 25\ncarl 40\n' | sort -k2 -n
alice 25
bob 30
carl 40

Here -k2 tells sort to compare starting at the second whitespace-delimited field, and -n tells it to compare that field numerically.

Multiple Sort Keys

You can chain multiple -k specifications, and sort applies them in order, using later keys only to break ties in earlier ones:

$ printf 'bob 30 eng\nalice 30 sales\ncarl 25 eng\n' | sort -k2,2n -k1,1
carl 25 eng
alice 30 sales
bob 30 eng

This sorted primarily by field 2 numerically (-k2,2n), then broke the tie between bob and alice (both age 30) using field 1 alphabetically (-k1,1). The field,field range syntax (-k2,2) restricts the key to exactly field 2 — without the explicit end field, -k2 alone actually means “from field 2 to the end of the line,” which is a common source of confusion when you expect it to sort by just one column.

Custom Field Delimiter: -t

By default, sort -k splits fields on whitespace. For delimited data like CSV or colon-separated files, -t sets a custom delimiter:

$ printf 'a:3\nb:1\nc:2\n' | sort -t: -k2 -n
b:1
c:2
a:3

I use this constantly for sorting /etc/passwd-style colon-delimited data, or simple CSV files, by a specific column.

Merging Pre-Sorted Files: -m

If you already have multiple files that are each individually sorted, -m merges them efficiently without re-sorting from scratch — genuinely faster than concatenating and re-sorting for large pre-sorted datasets:

$ printf '1\n3\n5\n' > s1.txt
$ printf '2\n4\n6\n' > s2.txt
$ sort -m s1.txt s2.txt
1
2
3
4
5
6

Writing Output to a File Safely: -o

$ sort s1.txt -o sorted_s1.txt
$ cat sorted_s1.txt
1
3
5

Critically, -o is safe to use even when the output file is the same as the input file — sort reads the entire input into memory (or temp files, for huge inputs) before writing output, so sort -o file.txt file.txt works correctly as an in-place sort. This is not safe to do with shell redirection: sort file.txt > file.txt truncates the file to empty before sort ever reads it, destroying your data. This is one of the more common and painful mistakes I’ve seen newer sysadmins make, and -o exists specifically to avoid it.

Stable Sort: -s

By default, when multiple lines compare as equal under the sort key, their relative order isn’t guaranteed to be preserved. -s forces a stable sort, keeping equal-comparing lines in their original relative order:

$ printf 'b 2\na 1\nb 1\na 2\n' | sort -k1,1 -s
a 1
a 2
b 2
b 1

Notice the two b lines kept their original relative order (b 2 then b 1) rather than being further sorted by the second field — that’s exactly what -s guarantees, and it matters whenever downstream processing depends on preserving a secondary, unspecified ordering.

Ignoring Leading Whitespace: -b

$ printf '   apple\nbanana\n  cherry\n' | sort -b

-b ignores leading blanks when comparing, which matters for data that’s been indented or padded inconsistently — without it, leading spaces count as actual sortable characters and can produce unexpected ordering.

How sort Works Internally

  1. In-memory sort for small inputs. sort reads all input lines and sorts them using an efficient comparison-based algorithm (GNU sort uses a merge sort variant internally), entirely in memory, when the dataset fits comfortably.
  2. External merge sort for large inputs. When input exceeds available memory (sort estimates this based on a configurable buffer size), it switches to an external sorting strategy: split the input into chunks small enough to sort in memory, write each sorted chunk to a temporary file, then merge all the sorted chunks together. This is precisely why sort can handle files far larger than available RAM without failing outright.
  3. Temporary file location is controlled by the TMPDIR environment variable (defaulting to /tmp). On systems with a small or slow /tmp, redirecting TMPDIR to a larger, faster disk before sorting huge files can meaningfully improve performance and avoid disk-full errors.
  4. Locale-aware collation. By default, sort‘s comparison respects the current locale’s collation rules (LC_COLLATE), which can produce different orderings for the same input on different systems or under different locale settings — a subtle source of “why did this sort differently on that other server” bugs.

Locale Gotchas

This is worth its own callout because it’s caused me real confusion before: sort order under a locale like en_US.UTF-8 treats punctuation and case differently than the plain C/POSIX locale. For byte-for-byte reproducible sorting — especially in scripts meant to run identically across different systems — I explicitly force the C locale:

$ LC_ALL=C sort file.txt

This guarantees pure byte-value ordering regardless of what locale is configured on the machine running the script, which matters a lot for scripts shared across a fleet of servers that might not all have identical locale settings.

Practical Sysadmin Use Cases

Sorting disk usage output by human-readable size:

$ du -sh /var/log/* | sort -h

Finding the top N largest files:

$ du -ah /var/www | sort -rh | head -10

Sorting /etc/passwd by UID:

$ sort -t: -k3 -n /etc/passwd

Sorting process list by memory usage:

$ ps aux | sort -k4 -rn | head -10

Preparing input for join/comm, which require sorted files:

$ sort file1.txt -o file1.txt
$ sort file2.txt -o file2.txt
$ comm -12 file1.txt file2.txt

sort in Shell Scripts

A common pattern for deduplicating and ranking log data:

#!/bin/bash
# Top 10 most frequent IPs hitting an endpoint
grep "/api/login" access.log \
    | awk '{print $1}' \
    | sort \
    | uniq -c \
    | sort -rn \
    | head -10

This chains sort twice for different purposes: once to prepare input for uniq -c (which requires sorted input to count consecutive duplicates correctly), and again to rank the resulting counts numerically in descending order.

sort vs. uniq vs. awk for Ranking Data

ToolRole
sortEstablishes order, required before uniq can count duplicates correctly
uniq -cCounts consecutive identical lines (requires pre-sorted input)
awkArbitrary field-based aggregation, more flexible but more verbose for simple ranking

The sort | uniq -c | sort -rn chain shown above is such a common combination it’s practically a Unix idiom in its own right, worth memorizing outright.

Performance Optimization

Troubleshooting

Numbers sort in the wrong order: Default sort is lexicographic (string-based). Add -n for plain numeric sort, -h for human-readable size suffixes, or -V for version/natural sort with embedded numbers.

Sort order differs between machines running “the same” script: Almost always a locale mismatch. Force LC_ALL=C for reproducible, byte-value sorting independent of system locale configuration.

sort file.txt > file.txt produced an empty file: Shell redirection truncates the output file before the command reads it. Use sort -o file.txt file.txt instead, which is safe for in-place sorting.

-k2 isn’t sorting by just field 2 the way I expected: -k2 alone means “from field 2 to the end of the line.” Use the explicit range -k2,2 to restrict the sort key to exactly one field.

Compatibility Across Distributions

GNU sort (part of coreutils) is standard across virtually all mainstream Linux distributions — Ubuntu, Debian, Fedora, RHEL/CentOS/Rocky, Arch, openSUSE — with identical behavior since they share the same upstream coreutils project. As with grep, the meaningful compatibility gap is with non-GNU implementations (BSD/macOS sort), which lack some GNU-specific extensions like -h (human-numeric), -V (version sort), and --parallel. Scripts intended to be portable beyond Linux should verify these flags are available or provide a fallback.

Summary

sort orders lines of text according to a comparison rule you control through its options — plain lexicographic by default, but numeric (-n), human-readable (-h), version-aware (-V), or month-aware (-M) as needed, with field-level precision via -k and -t for structured data. Its internal external-merge-sort design means it scales gracefully to files far larger than available memory, and paired correctly with uniq, join, or comm (all of which expect sorted input), it forms the backbone of a huge amount of everyday Linux text processing. The two mistakes worth permanently avoiding: destroying a file with sort file > file instead of sort -o file file, and forgetting that default sort order is lexicographic when you actually needed numeric.

References

Exit mobile version