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
  • 32,499
  • 12
  • 130
  • 417

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
  • 90,796
  • 20
  • 120
  • 197
Charles Duffy
  • 257,635
  • 38
  • 339
  • 400
  • 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 '' < – 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
  • 223,042
  • 33
  • 293
  • 407
  • 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