Dynamic case statement in bash

13,450

Solution 1

A case statement is probably not the right tool for the job. If you store the awk output in an array then you can loop through the array to find if a choice is in it, and as a bonus can figure out which index that is, too.

#!/bin/bash

# Store command output in an array so each word is a separate array item.    
list=($(echo $'red\ngreen\nblue'))
my_var=blue

for ((i = 0; i < ${#list}; i++)); do
    if [[ ${list[$i]} = $my_var ]]; then
        echo "found at index $i"
        break
    fi
done

if ((i == ${#list})); then
    echo "not found"
fi

Solution 2

You can create a dynamic case statement in bash by doing the following:

1) ensure the list is PIPE (|) seperated. IE. red|green|blue

2) wrap your case statement in an eval

For example:

valid="red|green|blue"

eval "case \"$choice\" in
    $valid)
        echo do something good here
        ;;
    *)
        echo invalid colour
        ;;
esac"

This works for simple variable processing, I can not guarantee this will work in all cases.

Solution 3

You can't do this with a case statement, but it's easy enough to set up your own helper to check for list membership.

# stub to simulate this arbitrary call
my_awk_command() { printf '%s\n' red green blue; }
# helper to check list membership
list_contains() {
  local tgt="$1"; shift
  while (( $# )); do
    if [[ $1 = "$tgt" ]] ; then
      return 0
    fi
    shift
  done
  return 1
}

# the below is Bash 4 functionality; see BashFAQ #1 on how to replace it
readarray -t awk_output < <(my_awk_command)

if list_contains "$my_var" "${my_awk_command[@]}"; then
  ...something...
elif [[ "$my_var" = something_else ]] ; then
  ...something else...
fi

Solution 4

You can approach this in a couple of different hacky ways:

pattern=($(awk_command))     # red\ngreen\nblue\n
saveIFS=$IFS
IFS='|'
pattern="^(${pattern[*]})$"  # ^(red|green|blue)$  (perhaps hackish)
IFS=$saveIFS

# simple regex match if statement (not hackish)
if [[ $var =~ $pattern ]]
then
    do_something
fi

# or a backwards case statement (very hackish)
case 1 in    # this could be a variable or a command substitution
    $([[ $var =~ $pattern]] && echo 1) )  # the echo 1 could be another command or the 1 could be yet another variable
        do_something;;
    * )
        do_default;;
esac
Share:
13,450
infra.user
Author by

infra.user

Updated on June 04, 2022

Comments

  • infra.user
    infra.user almost 2 years

    I'm trying to figure out how to create a dynamic case statement in a bash script.

    For example, let's say I have the output of an awk statement with the following contents

    red
    green
    blue
    

    In this scenario, the output can change at any time.

    I'm trying to then execute different logic if a value is included in this awk output.

    So if the data above is in $list, then I'd conceptually like to do something like:

    case "${my_var}" in
        $list)
            .....
        something_else)
            .....
    esac
    

    I'm trying to use this to build a dynamic custom tab completion function (see http://www.debian-administration.org/article/An_introduction_to_bash_completion_part_2 for some background).

    Any ideas?

    Thanks.