1

In bash, there is an array like this:

arr=(12345_34, 5_32134, 8_123, 13_1234)

And I'd like to sort (decreasing order) this array based on the numbers before the underscore. So the desired result is the following:

(12345_34, 13_1234, 8_123, 5_32134)

I tried sort -t _-k 2 -g $arr

phuclv
  • 37,963
  • 15
  • 156
  • 475

2 Answers2

2
arr=(12345_34 5_32134 8_123 13_1234)
readarray -t arr_sorted < <(printf '%s\n' "${arr[@]}" | sort -r -t _ -g)
declare -p arr_sorted

...properly emits as output the ordering specified in the question:

declare -a arr_sorted=([0]="12345_34" [1]="13_1234" [2]="8_123" [3]="5_32134")

If you need to target versions of bash too old to have readarray, a while read loop can substitute, with considerable loss of terseness:

# define the input array
arr=(12345_34 5_32134 8_123 13_1234)

# generate a sorted version
arr_sorted=( )
while IFS= read -r item; do
  arr_sorted+=( "$item" )
done < <(printf '%s\n' "${arr[@]}" | sort -r -t _ -g)

# print the sorted version to demonstrate that we built it correctly
declare -p arr_sorted
dawg
  • 98,345
  • 23
  • 131
  • 206
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Bash 4.0+ only... – dawg Oct 24 '20 at 01:53
  • @dawg, ...fair point. Edited to show the legacy approach as well. – Charles Duffy Oct 24 '20 at 02:07
  • Which still expands still expands `*` as a glob if that is present in the array... – dawg Oct 24 '20 at 02:19
  • You can also fallback to `IFS=$'\n' read -a arr_sorted -d '' < <(printf '%s\n' "${arr[@]}" | sort -r -t _ -g)` and it populates the array all at once without looping with shell, and works with older bash without mapfile. – Léa Gris Oct 24 '20 at 02:26
  • @dawg, pardon? There's a reason it quotes `"$item"`. No, there's no glob expansion here. – Charles Duffy Oct 24 '20 at 04:10
  • 1
    @LéaGris, ...when using that practice, btw, I typically put a `&& printf '\0'` inside the process substitution; that way the `read` exits with a successful status if-and-only-if the process substitution does. – Charles Duffy Oct 24 '20 at 04:11
  • @CharlesDuffy: Hmmm. If I add a `*` to `arr` on `3.2.57(1)-release` on a Mac and run your legacy version - yes, it expands that glob. Same [HERE](https://rextester.com/SQFR65983) but [NOT HERE](https://tio.run/##RY5BS8NAEIXP5lc81kpTJYc0EQQbUEGxF4UGvKiEbXY0gXS3zI4W0f72uKuHXubBzPfevLX23ThS2zmoyffNdX3fPN2u6uXjw14lmrlK83lRnjdFiTDmedCLJqyQF1FKnM6SyDXesZCpUsySXdcPhOVdXYFJG2SMXmhzCeOSowN7FmA1iRcVTMZZwgKLdMu9lTdMT/yLncZSwfF89bpX@EE0xrhM0CB7D6@P8cdDOsJ/LD6Jfe8sxMHQxlkvrIUCoQU7wvqjHyQUQuuYqZXhKzHUDpoJ2RaHeuP4Cw) Ideas? – dawg Oct 24 '20 at 13:06
  • OK: I got it. When I put `*` in the arr *declaration* as an unquoted `*` it expands at that point. DUH! tio.run must have some modification that catches that... – dawg Oct 24 '20 at 13:36
0

Try this:

sorted=($(printf '%s\n' "${arr[@]}" | sort -nr))
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Consider some [BashPitfalls #50](http://mywiki.wooledge.org/BashPitfalls#hosts.3D.28_.24.28aws_....29_.29) grumbling to have also taken place. – Charles Duffy Oct 24 '20 at 01:36
  • @CharlesDuffy: I preemptively grumble every time I write Bash code. That way I'm never disappointed by the endless gotchas. Thanks for pointing it out though. – John Zwinck Oct 24 '20 at 01:43
  • If there is `*` in the array... – dawg Oct 24 '20 at 01:52