sort

Sort lines: alphabetically, numerically, or by field

Updated 2026-08-13

sort orders the lines of a file or stream and writes the result to stdout. On its own that sounds trivial, but two things trip people up constantly: the default order is lexicographic, not numeric, and sort compares whole lines unless you tell it which field to look at.

Plain sort on a file of numbers puts 10 before 2, because it's comparing the characters '1' and '2', not the values ten and two. -n switches to numeric comparison; -h does the same but also understands K/M/G suffixes, which is what you want when sorting du -h or ls -lh output.

For anything with columns (CSV, ps output, du output) -k picks which field to sort by instead of the whole line: -k2 sorts by the second field, -t, changes the field separator from whitespace to a comma. Combine -k with -n or -h to sort a specific numeric column correctly instead of falling back to lexicographic order on it by accident.

-u deduplicates while sorting (cheaper than piping to uniq separately when you don't need the unsorted order preserved). -r reverses whatever order you asked for. -c checks whether a file is already sorted without printing anything, useful in scripts as a precondition check.

Case matters too: default comparison is case-sensitive, so every uppercase letter sorts before every lowercase one in the ASCII table, so Cherry lands before apple. -f folds case before comparing, which is almost always what you want for sorting words a human will read.

Sample files used on this page

Every example below was run against these files. Recreate them to follow along.

wordlist.txt duplicates and mixed case, to show what -u and -f do

banana
apple
Cherry
apple
date
banana
apple
Elderberry
cherry

numbers.txt deliberately ordered so lexicographic and numeric sorting disagree

10
2
33
4

scores.txt three whitespace-separated fields; the score is field 3

Team Alpha 89
Team Beta 42
Team Gamma 100
Team Delta 7

sizes.txt human-readable sizes in field 2, spanning bytes to gigabytes

report.txt 1.2K
access.log 890
photos.tar.gz 4.3G
backup.sql 128M
notes.txt 340

users.csv comma-separated, with a header row that must stay on top

name,age,department
Alice,34,Engineering
Bob,29,Sales
Carol,41,Engineering
Dave,25,Marketing
Erin,38,Sales
Frank,31,Engineering

people.csv two records per department, for the stable-sort and dedupe-by-field examples

Carol,Eng
Bob,Sales
Alice,Eng
Dave,Sales

access.log standard combined log format: IP is field 1, status field 8, bytes field 9

203.0.113.5 - - [13/Aug/2026:09:12:01] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:12:03] "GET /style.css HTTP/1.1" 200 231
198.51.100.7 - - [13/Aug/2026:09:14:22] "GET /index.html HTTP/1.1" 200 512
198.51.100.7 - - [13/Aug/2026:09:14:25] "GET /missing.html HTTP/1.1" 404 162
203.0.113.5 - - [13/Aug/2026:09:15:47] "GET /index.html HTTP/1.1" 200 512
192.0.2.44 - - [13/Aug/2026:09:16:03] "POST /login HTTP/1.1" 302 0
192.0.2.44 - - [13/Aug/2026:09:16:04] "GET /dashboard HTTP/1.1" 200 4021
198.51.100.7 - - [13/Aug/2026:09:18:51] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:19:10] "GET /api/status HTTP/1.1" 500 89
192.0.2.44 - - [13/Aug/2026:09:20:33] "GET /dashboard HTTP/1.1" 200 4021

data.txt fixed 1-character prefix then a 3-letter code, for the -k1.2,1.4 example

xAAAy
xCCCa
xBBBz

setA.txt / setB.txt two overlapping sets, for the comm example - b and a are in both

==> setA.txt <==
a
c
b

==> setB.txt <==
b
d
a
41 outputs, collapsed by default

Default order: lexicographic, not numeric

With no flags, sort compares lines character by character. That's exactly what you want for words, and almost never what you want for numbers.

Sort lines of text alphabetically

sort wordlist.txt

Default comparison, case-sensitive. Every uppercase letter sorts before every lowercase one in ASCII.

Show output
Cherry
Elderberry
apple
apple
apple
banana
banana
cherry
date

Fold case before comparing

sort -f wordlist.txt

-f (--ignore-case) compares case-insensitively, so Cherry and cherry sort next to each other instead of at opposite ends of the file.

Show output
apple
apple
apple
banana
banana
Cherry
cherry
date
Elderberry

Numeric and human-readable sorting

-n for plain numbers, -h when the numbers carry K/M/G suffixes, -V for version-style strings, -M for month names.

Sort du or ls -h output by actual size

sort -k2 -h sizes.txt

-h understands K/M/G suffixes as multipliers, so 890 < 1.2K < 128M < 4.3G comes out in the order a human expects.

Show output
notes.txt 340
access.log 890
report.txt 1.2K
backup.sql 128M
photos.tar.gz 4.3G

The mistake: -h without picking the size field

sort -h sizes.txt

Without -k2, sort -h compares from the start of each line - the filename - which isn't a number at all, so this silently falls back to sorting alphabetically by filename instead of by size. Verified: this produces access.log, backup.sql, notes.txt, photos.tar.gz, report.txt - alphabetical order, not size order.

Sort filenames with embedded numbers correctly

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

-V (version sort) treats digit runs as numbers, so file2.txt sorts before file10.txt instead of after it - the opposite of what plain sort would do.

Show output
file1.txt
file2.txt
file10.txt

Sort by month name

printf 'Mar 3\nJan 1\nFeb 2\n' | sort -M

-M recognises the first three letters of month names (Jan, Feb, Mar...) and sorts chronologically instead of alphabetically.

Show output
Jan 1
Feb 2
Mar 3

Sorting by field with -k

-k N sorts by the Nth field instead of the whole line. -t changes what counts as a field separator.

Sort a scoreboard by score

sort -k3 -n scores.txt

-k3 picks the third whitespace-separated field; -n makes that comparison numeric.

Show output
Team Delta 7
Team Beta 42
Team Alpha 89
Team Gamma 100

Same scoreboard, highest first

sort -k3 -rn scores.txt

-r reverses whatever order the rest of the flags produce - combine it with -n rather than trying to sort ascending and pipe through tac.

Show output
Team Gamma 100
Team Alpha 89
Team Beta 42
Team Delta 7

Sort CSV data by a numeric column

(head -1 users.csv; tail -n +2 users.csv | sort -t, -k2 -n)

-t, treats a comma as the field separator instead of whitespace. Keeping the header out of the sort with head/tail is the standard trick for not sorting it into the middle of the data.

Show output
name,age,department
Dave,25,Marketing
Bob,29,Sales
Frank,31,Engineering
Alice,34,Engineering
Erin,38,Sales
Carol,41,Engineering

Sort by one field, then break ties with another

(head -1 users.csv; tail -n +2 users.csv | sort -t, -k3,3 -k2,2n)

The -k options apply in order: group by department (field 3) first, then sort each group by age (field 2) numerically. -k3,3 means 'field 3 to field 3' - without the end bound, -k3 alone would sort by field 3 through the end of the line.

Show output
name,age,department
Frank,31,Engineering
Alice,34,Engineering
Carol,41,Engineering
Dave,25,Marketing
Bob,29,Sales
Erin,38,Sales

Ignore leading whitespace when comparing

printf '   banana\napple\n  cherry\n' | sort -b

-b (--ignore-leading-blanks) skips indentation before comparing, so inconsistently-indented input still sorts by content.

Show output
apple
   banana
  cherry

Deduplicating, reversing, and combining sorted files

Sort and deduplicate in one pass

sort -u wordlist.txt

-u drops duplicate lines after sorting - cheaper than sort | uniq when you don't need the two steps separately, and it needs the file sorted anyway.

Show output
Cherry
Elderberry
apple
banana
cherry
date

The same thing, long-form flag

sort --unique wordlist.txt

--unique is the exact same flag as -u - useful to know when reading someone else's script that spells it out.

Sort a file in place safely

sort -u -o wordlist.txt wordlist.txt

-o lets the output filename be the same as the input - sort reads the whole file into memory before opening the output, so this doesn't truncate the input out from under itself the way sort wordlist.txt > wordlist.txt would. It prints nothing: the result goes to the file.

Reverse the sort order

sort -r wordlist.txt

-r reverses the final order, applied after any other comparison flags.

Show output
date
cherry
banana
banana
apple
apple
apple
Elderberry
Cherry

Merge two files that are already sorted

sort -m -n numbers-sorted.txt other-sorted.txt

-m merges pre-sorted inputs without re-sorting each one from scratch - much cheaper than cat-ing them together and sorting the result, on large files.

Show output
1
2
4
5
10
33
50

Checking and writing sorted output

Check whether a file is already sorted

sort -c numbers.txt

-c prints nothing and exits 0 if the file is already in sorted order for the comparison rules given - here, default lexicographic order, under which 10, 2, 33, 4 happens to already qualify.

Write sorted output to a new file

sort -o wordlist-sorted.txt wordlist.txt

-o is the safe way to 'sort in place' - sort > wordlist.txt would truncate the file before reading it, destroying the input. -o opens the output only after reading the input fully, so input and output can even be the same filename.

Sort output from another command

find . -maxdepth 1 -name 'numbers*' | sort

sort reads stdin when no filename is given, same as most Unix text tools - the standard way to get find's arbitrary directory-walk order into something predictable.

Show output
./numbers-pair.txt
./numbers-sci.txt
./numbers-sorted.txt
./numbers.txt

Sort NUL-separated input safely

find . -maxdepth 1 -name '*.txt' -print0 | sort -z

-z reads and writes NUL-terminated records instead of newline-terminated ones - the correct pairing with find -print0 and xargs -0 for filenames that might contain newlines.

Force byte-order sorting regardless of locale

LC_ALL=C sort wordlist.txt

Sort order can change between locales (some locales fold case or ignore punctuation differently by default). LC_ALL=C forces plain byte-value comparison - the reproducible choice for scripts, even on a system whose default locale sorts differently.

Show output
Cherry
Elderberry
apple
apple
apple
banana
banana
cherry
date

More field-sorting patterns

-k accepts a lot more than a bare field number: character ranges within a field, colon or tab separators, stability guarantees.

Sort colon-separated data, the /etc/passwd shape

sort -t: -k3 -n /etc/passwd | head -3

-t: splits on colons; -k3 -n sorts by the numeric UID field. The same shape works for any colon-delimited system file.

Show output
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin

Sort on part of a field, not the whole thing

sort -k1.2,1.4 data.txt

1.2,1.4 means field 1, characters 2 through 4 - sorts on a slice inside a field rather than the field boundary. Here it sorts by the 3-letter code embedded after a fixed 1-character prefix.

Show output
xAAAy
xBBBz
xCCCa

Keep equal-key rows in their original relative order

sort -s -t, -k2,2 people.csv

Default sort doesn't guarantee what happens to rows that compare equal - -s (--stable) guarantees ties keep their original relative order, which matters when you sort by one field but still care about a secondary implicit order.

Show output
Carol,Eng
Alice,Eng
Bob,Sales
Dave,Sales

Sort a tab-separated file

sort -t$'\t' -k2 data.tsv

$'\t' is bash's way of writing a literal tab in a single-quoted-feeling argument - plain -t ' ' works too but is easy to mistype invisibly.

Show output
b	a
a	z

General numeric sort handles scientific notation

sort -g numbers-sci.txt

-n treats anything it can't parse as zero; -g (--general-numeric-sort) additionally understands scientific notation like 1e2, at some extra performance cost. Use -n unless you specifically have exponents in the data.

Show output
3
2.5e1
1e2

Deduplicate by one field, keeping one row per key

sort -t, -k2,2 -u people.csv

-u after a -k range dedupes on just that field, not the whole line - one row per distinct field-2 value survives (whichever sorts first once the rest of the line is compared as the tiebreak).

Show output
Carol,Eng
Bob,Sales

Multiple -k options without explicit end bounds

printf 'b 2 x\na 2 y\nc 1 z\n' | sort -k2 -k1

Without a comma, each -k extends to the end of the line, so the second -k1 only breaks ties left over after the first key already covered everything from field 2 onward. For precise multi-column sorts, always give explicit N,N ranges instead.

Show output
c 1 z
b 2 x
a 2 y

See exactly what sort is comparing and why

sort -n --debug numbers-pair.txt

--debug annotates the comparison it performed under each line - invaluable when a sort order looks wrong and you can't tell whether it's reading the field, locale, or numeric parsing you expect.

Show output
sort: text ordering performed using simple byte comparison
sort: numbers use '.' as a decimal point in this locale
2
_
_
10
__
__

Sorting real log and system data

Applying -k to data you didn't design yourself - a web server access log with quoted fields.

Group log entries by HTTP status code

sort -k8 -n access.log

Field 8 in a standard combined log line is the status code - the quoted request eats fields 5–7, so counting whitespace-separated fields by hand, rather than by meaning, is what finds it.

Show output
192.0.2.44 - - [13/Aug/2026:09:16:04] "GET /dashboard HTTP/1.1" 200 4021
192.0.2.44 - - [13/Aug/2026:09:20:33] "GET /dashboard HTTP/1.1" 200 4021
198.51.100.7 - - [13/Aug/2026:09:14:22] "GET /index.html HTTP/1.1" 200 512
198.51.100.7 - - [13/Aug/2026:09:18:51] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:12:01] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:12:03] "GET /style.css HTTP/1.1" 200 231
203.0.113.5 - - [13/Aug/2026:09:15:47] "GET /index.html HTTP/1.1" 200 512
192.0.2.44 - - [13/Aug/2026:09:16:03] "POST /login HTTP/1.1" 302 0
198.51.100.7 - - [13/Aug/2026:09:14:25] "GET /missing.html HTTP/1.1" 404 162
203.0.113.5 - - [13/Aug/2026:09:19:10] "GET /api/status HTTP/1.1" 500 89

Find the biggest responses a server sent

sort -k9 -rn access.log | head -n 3

Field 9 is response size in bytes. Combining -r, -n, and head is the standard 'top N' shape for any sortable log column.

Show output
192.0.2.44 - - [13/Aug/2026:09:20:33] "GET /dashboard HTTP/1.1" 200 4021
192.0.2.44 - - [13/Aug/2026:09:16:04] "GET /dashboard HTTP/1.1" 200 4021
203.0.113.5 - - [13/Aug/2026:09:15:47] "GET /index.html HTTP/1.1" 200 512

Sort IPv4 addresses in actual numeric order

sort -t. -k1,1n -k2,2n -k3,3n -k4,4n access.log

Chaining a numeric -k per octet is the fix for plain sort, which compares addresses as text and puts 10.x ahead of 9.x.

Combining sort with other commands

Rank files by line count

wc -l *.txt | sort -rn | head -5

wc -l prints a count-then-filename line per file plus a total; sort -rn puts the biggest counts first, total included since it's just another number in the stream.

Show output
  92 total
  40 report.txt
   9 wordlist.txt
   9 wordlist-sorted.txt
   5 sizes.txt

Count how often each line appears

sort wordlist.txt | uniq -c | sort -rn

The classic frequency-count pipeline: sort groups identical lines together so uniq -c can count them, then a second sort orders by that count. See the uniq page for -c and its other counting flags.

Show output
      3 apple
      2 banana
      1 date
      1 cherry
      1 Elderberry
      1 Cherry

Preview sorted output while also saving it

sort numbers.txt -n | tee numbers-sorted-final.txt | head -n 2

tee writes the full sorted stream to a file and still passes it through to head, so you can sanity-check the first few lines without a second pass over the data.

Show output
2
4

Read from a heredoc instead of a file

sort <<EOF
zebra
apple
mango
EOF

sort reads stdin exactly the same whether it's piped, redirected from a file, or fed by a heredoc - useful when the data to sort is generated inline in a script.

Show output
apple
mango
zebra

Confirm sort -u and sort | uniq agree

diff <(sort -u wordlist.txt) <(sort wordlist.txt | uniq)

No output and exit 0 means the two files are identical - confirms -u is a safe shortcut for sort | uniq when you don't need the two steps split apart in a pipeline.

Get the smallest value in a list

sort -n numbers.txt | head -n 1

sort plus head -n 1 is the standard shell idiom for a minimum - there's no separate 'min' utility.

Show output
2

Get the largest value in a list

sort -rn numbers.txt | head -n 1

Same idiom reversed: sort descending, take the first line, for a maximum instead.

Show output
33

Count how many distinct values there are

sort -u wordlist.txt | wc -l

Sort down to unique lines, then count them - the fastest way to answer 'how many different values' without writing a script.

Show output
6

Compare two sorted sets to see what's only in one

comm -3 <(sort setA.txt) <(sort setB.txt)

comm isn't sort, but it requires sorted input to work correctly - a common reason to run sort even when the sorted order itself isn't what you want to keep. -3 suppresses lines common to both, leaving only what's unique to each side.

Show output
c
	d

Performance and large-file options

Flags that matter once a file is too big to sort comfortably in memory.

Use multiple CPU cores for the sort

sort --parallel=2 -n numbers.txt

--parallel caps how many sort threads run at once - helps on large files, does nothing measurable on a file this small.

Show output
2
4
10
33

Set how much memory sort is allowed to use

sort -S 1K -n numbers.txt

-S bounds sort's in-memory buffer; once a sort exceeds it, sort spills to temporary files on disk and merges them instead of holding everything in RAM at once. Same correct result either way.

Show output
2
4
10
33