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.
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
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
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.
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