1

How would one evaluate priority of items in an array such that you only process the highest priority item that exists? Let me try to explain, if I had an array

set -A array low medium none high 

this array is created running a command in which the array could contain any or all of the listed elements. What I want to do is evaluate on whatever the highest priority element it finds and stop looping through the array without exiting the script. In other words if low and high exist, only evaluate on high and do not keep looping through the array. If medium and low exist, only evaluate on medium and stop looping.

The only way I've found to do this so far is a horrible kludge of multiple loops entered in the order I am looking for the element. If the element is found, then I exit before it can get to the next loop but I need to not have an exit or return if that makes sense.

Here's a sample:

#!/bin/sh

set -A array low medium none high

high() { 
        printf "High\n" 
}
medium() { 
        printf "Medium\n" 
}
low() { 
        printf "Low\n" 
}
none() { 
        printf "None\n" 
}

for i in ${array[*]}; do if [ ${i} = "high" ]; then high; exit 0; fi done
for i in ${array[*]}; do if [ ${i} = "medium" ]; then medium; exit 0; fi done
for i in ${array[*]}; do if [ ${i} = "low" ]; then low; exit 0; fi done
for i in ${array[*]}; do if [ ${i} = "none" ]; then none; exit 0; fi done

With the above code, if you change the array and take out any of the elements it is almost forced to evaluate based on the hierarchy I evaluate on. If high, low, medium, none exist it'll print high and exit. If you take out high and medium it'll print low and exit.

I have another script reading this so if I exit, it exits the entire chain including the parent that loads this so I'm trying to figure out how I can stop evaluating any elements from the array once I've found the highest priority for lack of a better description.

If i take out the exists it just hits each loop and gives output for each loop. I've tried unsuccessfully with elif and else but it always evaluates every element.

Any ideas? Is it even possible to have it stop without exiting the script?

  • 1
    use break instead of exit. – mosvy Apr 18 at 18:27
  • Also, use the correct path to ksh on your system in the #!-line. /bin/sh will generally not support arrays. – Kusalananda Apr 18 at 18:29
  • true regarding path. The rub is that this is on AIX so default shell is korn. I'm just so used to it being korn regardless I seldom explicitly quilify ksh. – Lonny Selinger Apr 18 at 19:00
  • @LonnySelinger Even if /bin/sh is Korn shell on your system, it would be Korn shell running in POSIX mode, which would quite likely be different from ksh. – Kusalananda Apr 18 at 19:04
0
#!/bin/ksh

high ()   echo High
medium () echo Medium
low ()    echo Low
none ()   echo None

set -A array low medium none high

for level in high medium low none; do
    for elem in "${array[@]}"; do
        if [ "$elem" = "$level" ]; then
            "$level"
            break 2
        fi
    done
done

This is a double loop. The outer loop loops over the levels in order of importance. The inner loop loops over the elements in the array, looking for an element that is equal to the current level.

If an element is found that corresponds to the current level, the function with the same name as the level is called and the two loops are exited.

Note that in the general case, you'd want to loop over "${array[@]}" rather than ${array[*]} as using [*] (and leaving the expansion unquoted) would do both word splitting and filename globbing on the strings in the array. The expression "${array[@]}" would expand to the individually quoted elements of the array array.

Avoiding word splitting and filename globbing is also the reason for quoting both $elem and $level when comparing them etc.


A possibly quicker way to do this would be to first create an associative array with the element from the array as keys and then just do key lookups in that:

#!/bin/ksh

high ()   echo High
medium () echo Medium
low ()    echo Low
none ()   echo None

array=( low medium none high )

typeset -A lookup

for elem in "${array[@]}"; do
    lookup["$elem"]=1
done

for level in high medium low none; do
    if [ -n "${lookup[$level]}" ]; then
        "$level"
        break 
    fi
done

This avoids the double loop, which would be useful if either the array or the number of level, or both, are very long.

I've also switched to the more common =(...) assignment form for arrays.

  • Oy, I've been fighting with this for a day already. Thank you! worked like a charm! – Lonny Selinger Apr 18 at 19:01
  • I forgot to mention I also added set -o noglob as i ran into that earlier. I can see uses for both now that I'm digging deeper into some of my other functions. – Lonny Selinger Apr 18 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.