Linux Basic — Sorting Arrays

3 minute read

Sorting Arrays

Bash has no native sort() for arrays. The idiomatic pattern is “pipe the elements through sort, then capture them back with mapfile.” It’s a one-liner once you’ve seen it.


The Base Pattern

arr=(cherry apple banana)

mapfile -t sorted < <(printf '%s\n' "${arr[@]}" | sort)

echo "${sorted[@]}"
# apple banana cherry

Breakdown:

  • printf '%s\n' "${arr[@]}" — one element per line (%s\n repeats for every arg)
  • | sort — sort the lines
  • mapfile -t sorted < <(…) — read each line back into the sorted array (-t strips the trailing \n)

mapfile is much safer than sorted=( $(…) ), which would re-split on whitespace.


Sort Flavours

sort has the variety you need built in:

Flag Meaning
(none) alphabetical (LC_COLLATE)
-r reverse
-n numeric
-h human-numeric (1K, 2M, 3G)
-V version sort (1.2, 1.10)
-f case-insensitive
-u unique (dedupe)
-t SEP -k N sort by column N split on SEP

Examples:

arr=(b c A B a)
mapfile -t sorted     < <(printf '%s\n' "${arr[@]}" | sort)
mapfile -t sorted_ci  < <(printf '%s\n' "${arr[@]}" | sort -f)
mapfile -t reverse    < <(printf '%s\n' "${arr[@]}" | sort -r)

echo "default: ${sorted[*]}"     # default: A B a b c
echo "ci:      ${sorted_ci[*]}"  # ci:      a A b B c
echo "reverse: ${reverse[*]}"    # reverse: c b a B A

Numeric:

nums=(10 2 33 4 100)
mapfile -t s < <(printf '%s\n' "${nums[@]}" | sort -n)
echo "${s[@]}"          # 2 4 10 33 100

Version-aware (perfect for tag/release lists):

versions=(v1.10 v1.2 v1.9 v2.0)
mapfile -t s < <(printf '%s\n' "${versions[@]}" | sort -V)
echo "${s[@]}"          # v1.2 v1.9 v1.10 v2.0

Deduplicate — sort -u

items=(apple banana apple cherry banana)

mapfile -t unique < <(printf '%s\n' "${items[@]}" | sort -u)
echo "${unique[@]}"     # apple banana cherry

sort -u is the simplest dedupe — it also sorts. For “dedupe while preserving order” use an associative-array-as-set:

declare -A seen
ordered=()
for x in "${items[@]}"; do
    if [[ ! -v seen[$x] ]]; then
        seen[$x]=1
        ordered+=("$x")
    fi
done
echo "${ordered[@]}"    # apple banana cherry      (first-seen order)

Sort an Associative Array

You can sort by keys or by values.

By key (alphabetical)

declare -A cfg=([host]=example.com [port]=8080 [debug]=true)

for k in $(printf '%s\n' "${!cfg[@]}" | sort); do
    printf '%-8s = %s\n' "$k" "${cfg[$k]}"
done
# debug    = true
# host     = example.com
# port     = 8080

By value (e.g. counters, descending)

declare -A count=([apple]=3 [banana]=2 [cherry]=1)

for line in $(
    for k in "${!count[@]}"; do
        printf '%d %s\n' "${count[$k]}" "$k"
    done | sort -rn
); do
    echo "$line"
done
# 3 apple
# 2 banana
# 1 cherry

A cleaner version using mapfile:

mapfile -t lines < <(
    for k in "${!count[@]}"; do
        printf '%d %s\n' "${count[$k]}" "$k"
    done | sort -rn
)
printf '%s\n' "${lines[@]}"

Reverse an Array (No Sort)

arr=(a b c d)

# Method 1 — loop backwards
rev=()
for ((i=${#arr[@]}-1; i>=0; i--)); do
    rev+=("${arr[i]}")
done
echo "${rev[@]}"        # d c b a

# Method 2 — tac (line-oriented; needs printf one-per-line)
mapfile -t rev < <(printf '%s\n' "${arr[@]}" | tac)

On macOS tac isn’t installed; use tail -r or stick with method 1.


Sort by a Computed Key

If you have rich elements (e.g. lines) and want to sort by a derived field:

items=(
    "alice 42"
    "bob 17"
    "carol 99"
    "dan 5"
)

mapfile -t by_age < <(printf '%s\n' "${items[@]}" | sort -k2 -n)
printf '%s\n' "${by_age[@]}"
# dan 5
# bob 17
# alice 42
# carol 99

-k2 -n says “sort by column 2 numerically.” sort is far more powerful than people realise.


Stable Sort

sort is stable by default — equal keys keep their original order. If you want to be explicit, pass -s. Important when you sort by one field and don’t want secondary fields shuffled.

sort -s -k2 -n      # stable numeric sort by field 2

Common Mistakes

❌ Wrong ✅ Right Issue
sorted=( $(printf '%s\n' "${arr[@]}" \| sort) ) mapfile -t sorted < <(printf '%s\n' "${arr[@]}" \| sort) command sub splits on whitespace
echo "${arr[@]}" \| sort printf '%s\n' "${arr[@]}" \| sort echo joins with spaces — one line, can’t sort
sort on versions sort -V 1.10 sorts before 1.2 lexically
Manual dedup loop without an assoc array sort -u for sort+dedupe, or assoc-set for stable order hand-coded dedup is O(n²)
Forgetting -n on numeric sort -n 10 sorts before 2 lexically

Next → Array vs String

Updated: