How to Sort The String Array in Linux Bash Shell

How to sort an array in Bash

You don't really need all that much code:

IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS

Supports whitespace in elements (as long as it's not a newline), and works in Bash 3.x.

e.g.:

$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]

Note: @sorontar has pointed out that care is required if elements contain wildcards such as * or ?:

The sorted=($(...)) part is using the "split and glob" operator. You should turn glob off: set -f or set -o noglob or shopt -op noglob or an element of the array like * will be expanded to a list of files.

What's happening:

The result is a culmination six things that happen in this order:

  1. IFS=$'\n'
  2. "${array[*]}"
  3. <<<
  4. sort
  5. sorted=($(...))
  6. unset IFS

First, the IFS=$'\n'

This is an important part of our operation that affects the outcome of 2 and 5 in the following way:

Given:

  • "${array[*]}" expands to every element delimited by the first character of IFS
  • sorted=() creates elements by splitting on every character of IFS

IFS=$'\n' sets things up so that elements are expanded using a new line as the delimiter, and then later created in a way that each line becomes an element. (i.e. Splitting on a new line.)

Delimiting by a new line is important because that's how sort operates (sorting per line). Splitting by only a new line is not-as-important, but is needed preserve elements that contain spaces or tabs.

The default value of IFS is a space, a tab, followed by a new line, and would be unfit for our operation.

Next, the sort <<<"${array[*]}" part

<<<, called here strings, takes the expansion of "${array[*]}", as explained above, and feeds it into the standard input of sort.

With our example, sort is fed this following string:

a c
b
f
3 5

Since sort sorts, it produces:

3 5
a c
b
f

Next, the sorted=($(...)) part

The $(...) part, called command substitution, causes its content (sort <<<"${array[*]}) to run as a normal command, while taking the resulting standard output as the literal that goes where ever $(...) was.

In our example, this produces something similar to simply writing:

sorted=(3 5
a c
b
f
)

sorted then becomes an array that's created by splitting this literal on every new line.

Finally, the unset IFS

This resets the value of IFS to the default value, and is just good practice.

It's to ensure we don't cause trouble with anything that relies on IFS later in our script. (Otherwise we'd need to remember that we've switched things around--something that might be impractical for complex scripts.)

How to sort string array in descending order in Bash?

You can do so fairly easily making use of IFS (internal field separator), sort -r, and a little help from printf. Using command substitution you can output and sort the array and then simply read the sorted results back into nameArr. For instance:

#!/bin/bash

nameArr=("Leia", "Darth Vader", "Anakin", "Han Solo", "Yoda")

IFS=$'\n' ## only word-split on '\n'
nameArr=( $(printf "%s\n" ${nameArr[@]} | sort -r ) ) ## reverse sort

declare -p nameArr ## simply output the array

Example Use/Output

Calling the script results in the following:

$ bash revarr.sh
declare -a nameArr='([0]="Yoda" [1]="Leia," [2]="Han Solo," [3]="Darth Vader," [4]="Anakin,")'

note: don't forget to restore the default IFS=$' \t\n' (space, tab, newline) when done with the sort if your script continues.

Sorting an array of pathnames (strings) [Bash]

You can use sort -r with printf, where input containg glob string to match your filenames:

sort -r <(printf "%s\n" $input)

How to sort an array of strings using substring with Bash

Tell sort to use the second column numerically:

printf '%s\n' "${count_visits[@]}" | sort -k2n

Sort multiple column String array in bash

As long as there are no newline characters in any array element, it's straight-forward: Just printf the array into sort and capture the output:

mapfile -t sorted < <(printf "%s\n" "${arr[@]}" | sort -k2,2n -k1,1)

(The use of process substitution is to avoid having the mapfile run in a subshell, which wouldn't be helpful since the goal is to set the value of $sorted in this shell.)

If the array elements might contain newlines, then you could use NUL as a delimiter in the printf and the sort (option -z for sort), but you'd have to replace mapfile with an explicit loop because mapfile does not offer an option to change the line delimiter. read does (-d '' will cause read to use NUL as a line delimiter), but it only reads one line at a time.



Related Topics



Leave a reply



Submit