3

I have an array like this:

array=(1 2 7 6) 

and would like to search for the second largest value, with the output being

secondGreatest=6 

Is there any way to do this in bash?

1
  • 4
    What if array=(1 2 7 6 7)? What is the 2nd largest value, 6 or 7?CommentedJan 23, 2019 at 15:38

3 Answers 3

5
printf '%s\n' "${array[@]}" | sort -n | tail -2 | head -1 

Print each value of the array on it's own line, sort it, get the last 2 values, remove the last value

secondGreatest=$(printf '%s\n' "${array[@]}" | sort -n | tail -2 | head -1) 

Set that value to the secondGreatest variable.


Glenn Jackman had an excellent point about duplicate numbers that I didn't consider. If you only care about unique values you can use the -u flag of sort:

secondGreatest=$(printf '%s\n' "${array[@]}" | sort -nu | tail -2 | head -1) 
1
  • 5
    You could save one pipe if you reverse sort, so that the answer is always in second place e.g. printf '%s\n' "${array[@]}" | sort -rn | awk NR==2 (likely head + tail is more efficient though) or (at least with GNU Coreutils, which support the null delimiters) printf '%s\0' "${array[@]}" | sort -rzn | cut -d '' -f2CommentedJan 23, 2019 at 14:03
4

A bash-specific loop through the array could do it; you have to keep track of the largest and second-largest. The only other tricky part is to be careful about initializing those values; the largest value is initialized to the first element; the second-largest value is initialized the first time we see a value that's smaller than the largest value. Subsequently for the second-largest value, we only update it if it's strictly less than the current largest value:

#!/bin/bash array=(7 7 6 2 1) if [ "${#array[@]}" -lt 2 ] then echo Incoming array is not large enough >&2 exit 1 fi largest=${array[0]} secondGreatest='unset' for((i=1; i < ${#array[@]}; i++)) do if [[ ${array[i]} > $largest ]] then secondGreatest=$largest largest=${array[i]} elif (( ${array[i]} != $largest )) && { [[ "$secondGreatest" = "unset" ]] || [[ ${array[i]} > $secondGreatest ]]; } then secondGreatest=${array[i]} fi done echo "secondGreatest = $secondGreatest" 

It's still slower than calling out to sort, but it has the added benefit of picking the strictly-smaller second-largest value in the face of multiple high values (such as 7 and 7 above).

    0

    It's a good job for dc :

    array=(1 2 7 6) echo ${array[*]} | dc -f - -e ' [lasbdsa]sB [dla!>Bsc1z>A]sA lAx [secondGreatest=]nlbp' 

      You must log in to answer this question.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.