sort Command in Linux: Complete Guide to Sorting Data, Lines, and Parameters

sort command in Linux and it perimeters

I once spent twenty minutes staring at a script that was producing “wrong” results, only to realize the bug wasn’t in my logic at all — it was that I’d sorted a list of numbers as plain text, so 10 came before 2. That’s the kind of mistake sort will let you make quietly if you don’t understand its options, and it’s also exactly why I now treat sort as a command worth actually learning properly instead of just typing sort and hoping.

sort is one of those utilities that looks trivial and turns out to have real depth — numeric sorting, field-based sorting, custom delimiters, stable sorts, and even external merge-sort behavior for files too large to fit in memory.

What sort Does

sort reads lines of input (from files or STDIN) and writes them back out in sorted order. By default, it sorts lexicographically (essentially alphabetically, based on byte/character values).

sort [OPTION]... [FILE]...

I tested the default behavior:

$ printf "banana\napple\ncherry\n10\n2\n1\n" > words.txt
$ sort words.txt
1
10
2
apple
banana
cherry

Notice 10 sorted between 1 and 2 — that’s correct lexicographic (text) ordering, not numeric ordering, and it’s the single most common source of confusion for people new to sort.

Core Options and Parameters

-n — Numeric Sort

$ sort -n words.txt
apple
banana
cherry
1
2
10

I tested this and confirmed non-numeric lines sort before numeric ones under -n in GNU sort, and the numbers themselves are correctly ordered by value (1, 2, 10) rather than lexicographically.

-r — Reverse Order

$ sort -r words.txt
cherry
banana
apple
2
10
1

Combine with -n for reverse numeric sort: sort -rn file.txt.

-u — Unique (Remove Duplicate Lines)

$ printf "b\na\nb\nc\na\n" | sort -u
a
b
c

This both sorts and deduplicates in a single pass, which is more efficient than piping sort into uniq separately (though sort | uniq is a common and valid pattern too, especially when you need uniq‘s counting features).

-f — Case-Insensitive (Fold Case)

$ printf "Banana\napple\nCherry\n" | sort -f
apple
Banana
Cherry

Without -f, GNU sort in the default locale actually already does something similar due to locale-aware collation, but -f makes the case-insensitivity explicit and predictable regardless of locale settings.

-k FIELD — Sort by a Specific Field/Column

This is the option that unlocks most of sort‘s real power. By default, fields are separated by whitespace:

$ printf "3 charlie\n1 alpha\n2 bravo\n" > namedata.txt
$ sort -k1,1n namedata.txt
1 alpha
2 bravo
3 charlie

-k1,1n means “sort by field 1 through field 1, numerically.” The ,1 end-field matters — without it, sort -k1 would sort by field 1 onward to the end of the line, which can produce different results when later fields differ.

-t SEPARATOR — Custom Field Delimiter

For CSV-like or custom-delimited data:

$ printf "b,2\na,3\nc,1\n" | sort -t',' -k2,2n
c,1
b,2
a,3

I tested this against comma-separated data, sorting numerically by the second field — exactly the pattern I use constantly when sorting CSV exports by a specific column from the shell without pulling out a spreadsheet tool.

-M — Month Sort

Recognizes three-letter month abbreviations (Jan, Feb, Mar…) and sorts them in calendar order rather than alphabetical order:

$ printf "Mar\nJan\nFeb\n" | sort -M
Jan
Feb
Mar

Without -M, alphabetical order would incorrectly put “Feb” before “Jan” alphabetically-adjacent months in the wrong sequence for many datasets — this option exists specifically to fix that class of problem, common when parsing log timestamps or date-labeled data.

-h — Human-Readable Numeric Sort (Sizes Like 1K, 2M, 1G)

$ printf "10K\n2M\n1G\n500\n" | sort -h
500
10K
2M
1G

I tested this against mixed unit-suffixed values, and it correctly ordered them by actual magnitude rather than treating them as plain strings — extremely useful when sorting the output of du -h or df -h.

-c — Check Whether Input Is Already Sorted

$ printf "1\n2\n3\n" | sort -c && echo "sorted ok"
sorted ok

If the input isn’t sorted, sort -c exits with a non-zero status and prints a message identifying the first out-of-order line — handy in scripts or CI pipelines that need to validate sort order as a precondition without actually re-sorting or modifying anything.

-o FILE — Write Output to a File (Safely, Even the Same File)

sort -o namedata_sorted.txt namedata.txt

Unlike sort file.txt > file.txt, which truncates the file to zero bytes before sort even reads it (a classic and painful shell redirection trap), sort -o file.txt file.txt is safe to use to sort a file in place, because sort reads the entire input before opening the output file for writing.

-s — Stable Sort

Preserves the original relative order of lines that compare as equal, rather than allowing an arbitrary reordering among ties:

sort -s -k1,1 file.txt

This matters when you’re sorting by one field but want ties broken by “whatever order they originally appeared in” rather than an unpredictable tie-break.

--parallel=N — Control Parallelism

For very large files, GNU sort can use multiple threads:

sort --parallel=4 -T /tmp huge_file.txt -o sorted_output.txt

-T DIRECTORY — Temp Directory for External Sort

When a file is too large to fit in memory, sort uses an external merge sort, writing temporary chunks to disk. -T lets you redirect those temp files to a directory with more space (useful when /tmp is small or on a slow disk):

sort -T /mnt/scratch huge_file.txt -o sorted.txt

How sort Works Internally

For input that fits comfortably in memory, GNU sort loads it, sorts using an efficient in-memory algorithm, and writes the result. For input larger than available memory (or larger than the --buffer-size threshold), sort switches to an external merge sort: it reads the input in chunks small enough to fit in memory, sorts each chunk independently, writes each sorted chunk to a temporary file, and then performs a k-way merge of all the sorted chunks to produce the final sorted output. This is why sort can handle files far larger than available RAM without crashing — a genuinely elegant piece of engineering that’s easy to take for granted.

Field-based sorting (-k) works by having sort split each line into fields according to the delimiter rules (whitespace by default, or -t), then comparing only the specified field range between lines rather than the whole line.

Practical, Real-World Examples

1. Sorting Log Entries by Timestamp Field

sort -k1,2 -t' ' access.log

2. Finding the Largest Files in a Directory

du -ah /var/log | sort -rh | head -10

This lists the 10 largest files/directories under /var/log, human-readable and correctly sorted by actual size rather than string comparison.

3. Deduplicating and Sorting a List of IPs

awk '{print $1}' access.log | sort -u

4. Sorting CSV Data by a Numeric Column

sort -t',' -k3,3n data.csv

5. Multi-Key Sort (Sort by Department, Then by Salary Descending)

sort -t',' -k2,2 -k3,3nr employees.csv

This sorts alphabetically by field 2 (department) first, and within each department, sorts numerically descending by field 3 (salary) — a pattern that comes up constantly in reporting scripts.

6. Validating a File Is Sorted Before Feeding It to join

sort -c data1.txt && sort -c data2.txt && join data1.txt data2.txt

join requires sorted input on the join field, so validating with sort -c before running join avoids silently wrong output from unsorted input.

sort in Shell Scripting and Automation

A pattern I use for generating top-N reports from log data:

#!/bin/bash
awk '{print $1}' /var/log/nginx/access.log \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -20

This produces the 20 most frequent client IPs hitting the server — a classic sort | uniq -c | sort -rn combo that’s one of the most useful one-liners in all of log analysis.

Comparing sort to Related Commands

TaskBest Tool
Sorting linessort
Removing duplicates after sortinguniq (used after sort)
Field extraction before sortingawk or cut
Sorting by multiple complex keyssort -k with multiple -k flags
SQL-style joins on sorted datajoin

sort and uniq are almost always used together (sort | uniq), because uniq only removes adjacent duplicate lines — it has no concept of the whole file, so input must be sorted first for uniq to catch all duplicates.

Troubleshooting Common sort Issues

Numbers sorting in the wrong order — you forgot -n. Lexicographic sort treats "10" as coming before "2" because it compares character by character.

sort file.txt > file.txt produces an empty file — shell redirection truncates the output file before sort even runs; use sort -o file.txt file.txt instead.

Locale-dependent sort order surprises (e.g., accented characters sorting unexpectedly, or case sensitivity behaving inconsistently across machines) — force a consistent locale:

LC_ALL=C sort file.txt

LC_ALL=C forces pure byte-value sorting, which is faster and more predictable across systems than a locale-aware collation that can vary by machine configuration.

-k sort not behaving as expected — remember to specify the end field (-k2,2) rather than just the start field (-k2), since an open-ended key range extends to the end of the line and can change results when trailing fields differ.

Performance Optimization

For very large files, --parallel=N lets sort use multiple CPU cores during the sort phase, and --buffer-size=SIZE controls how much memory is used before falling back to disk-based external merge sort — increasing buffer size (when RAM allows) reduces the number of temporary files written and merged, improving throughput. LC_ALL=C also meaningfully speeds up sorts on large text files by avoiding locale-aware comparison overhead.

Security Implications

Be cautious with -o writing to files in shared or world-writable directories, and with -T pointing to temp directories — sort‘s temporary files during external merge sort can briefly contain the full (unsorted or partially sorted) contents of sensitive data on disk, so ensure the temp directory has appropriate permissions when sorting files containing secrets or PII.

Compatibility Across Distributions

sort is part of GNU coreutils and is standard on Ubuntu, Debian, Fedora, RHEL/CentOS, Arch, and openSUSE (tested here at coreutils 9.4). BSD/macOS sort supports the core POSIX options (-n, -r, -u, -k, -t) but lacks some GNU extensions like -h (human-numeric) and --parallel. If writing portable scripts, stick to POSIX-documented flags or check sort --version output to detect GNU versus BSD behavior.

A Closer Look at External Merge Sort

The external merge sort strategy sort falls back to for large files deserves a slightly deeper explanation, since it’s a genuinely elegant piece of classical computer science quietly doing real work behind a one-word command. When input exceeds the configured memory buffer, sort splits it into runs — chunks small enough to sort entirely in memory using an efficient comparison sort. Each sorted run is written out to a temporary file on disk. Once the entire input has been consumed and converted into some number of sorted temporary runs, sort performs a k-way merge: it opens all the temporary files simultaneously, and repeatedly picks the smallest (or largest, for -r) currently-available value across all of them, writing that value to the final output and advancing only the pointer for the file it came from. Because each individual run is already sorted, this merge step never needs to look backward — it can produce fully sorted output in a single forward pass across all the runs combined.

This approach guarantees sort never needs more memory than the buffer size for any single run, regardless of how large the total input is, at the cost of needing enough temporary disk space to hold the sorted runs before the final merge completes. This is exactly why -T (specifying a temp directory) matters on systems where /tmp is small, backed by tmpfs (RAM-backed, which could exhaust memory in a different way), or simply full — redirecting the temp files to a directory with adequate free disk space avoids sort failing partway through a large job with a disk-full error.

Locale-Aware Collation and Why It Sometimes Surprises People

GNU sort‘s default sorting behavior is locale-aware, meaning it uses the collation rules defined by your system’s configured locale (LANG/LC_COLLATE) rather than pure byte-value comparison. In many locales, this means punctuation, case, and accented characters are compared according to linguistically sensible rules rather than raw ASCII/Unicode code point order — for example, in some locales, "apple" and "Apple" might sort adjacent to each other rather than with all-uppercase words grouped separately, because case is treated as a secondary sorting criterion rather than a primary one.

This is usually the behavior you want for genuinely human-readable text sorted for a human audience, but it can produce results that look “wrong” if you’re expecting strict byte-order sorting, and it can also vary between machines with different locale configurations — a script that works predictably in one environment can produce a subtly different sort order in another. This is the underlying reason LC_ALL=C sort is such a commonly repeated piece of advice in scripting contexts: forcing the C locale disables locale-aware collation entirely, falling back to simple byte-value comparison, which is both faster (no collation rule lookups) and perfectly reproducible across any machine regardless of its configured locale.

Combining Multiple Sort Keys in Complex Reports

Beyond the two-key example shown earlier, sort supports chaining as many -k options as needed, which becomes genuinely powerful for generating structured, multi-level sorted reports directly from the command line without needing a database or spreadsheet:

sort -t',' -k1,1 -k2,2nr -k3,3 sales_data.csv

This sorts first alphabetically by field 1 (say, region), then numerically descending by field 2 (say, revenue) within each region, then alphabetically by field 3 (say, product name) as a final tiebreaker within matching revenue values — producing a fully deterministic, hierarchically organized report in a single command.

Summary

sort is far more capable than “alphabetize this file” — with -n/-h for correct numeric and size-based ordering, -k/-t for field-aware sorting on structured data, -u for dedup-while-sorting, and its internal external-merge-sort design for handling files larger than RAM, it’s a genuine data-processing tool, not just a display utility. Pairing it with uniq, awk, and join covers a huge fraction of everyday text-processing needs on the command line.

References

Exit mobile version