我正在尝试使用由以 a 分隔的不同字符串组成的变量|
作为case
语句测试。例如:
string="\"foo\"|\"bar\""
read choice
case $choice in
$string)
echo "You chose $choice";;
*)
echo "Bad choice!";;
esac
我希望能够输入foo
orbar
并执行语句的第一部分case
。然而,两者foo
都把bar
我带到了第二个:
$ foo.sh
foo
Bad choice!
$ foo.sh
bar
Bad choice!
使用"$string"
代替$string
没有什么区别。使用也不行string="foo|bar"
。
我知道我可以这样做:
case $choice in
"foo"|"bar")
echo "You chose $choice";;
*)
echo "Bad choice!";;
esac
我可以想到各种解决方法,但我想知道是否可以case
在 bash 中使用变量作为条件。这可能吗?如果可能的话,如何实现?
答案1
bash 手册指出:
[ [(] 模式 [ | 模式 ] ... ) 列表中的大小写单词 ;; ] ... esac
检查的每个模式都使用波形符扩展、参数和变量扩展、算术替换、命令替换和进程替换来扩展。
没有“路径名扩展”
因此:模式不会通过“路径名扩展”进行扩展。
因此:模式不能包含“|”里面。唯一:两个模式可以用“|”连接。
这有效:
s1="foo"; s2="bar" # or even s1="*foo*"; s2="*bar*"
read choice
case $choice in
$s1|$s2 ) echo "Two val choice $choice"; ;; # not "$s1"|"$s2"
* ) echo "A Bad choice! $choice"; ;;
esac
使用“扩展通配符”
但是,与使用“路径名扩展”规则word
相匹配。 以及“扩展通配符”pattern
这里,这里和,这里允许使用交替(“|”)模式。
这也有效:
shopt -s extglob
string='@(foo|bar)'
read choice
case $choice in
$string ) printf 'String choice %-20s' "$choice"; ;;&
$s1|$s2 ) printf 'Two val choice %-20s' "$choice"; ;;
*) printf 'A Bad choice! %-20s' "$choice"; ;;
esac
echo
字符串内容
foo
下一个测试脚本显示匹配包含任一或任意位置的所有行的模式bar
是'*$(foo|bar)*'
或两个变量$s1=*foo*
和$s2=*bar*
测试脚本:
shopt -s extglob # comment out this line to test unset extglob.
shopt -p extglob
s1="*foo*"; s2="*bar*"
string="*foo*"
string="*foo*|*bar*"
string='@(*foo*|*bar)'
string='*@(foo|bar)*'
printf "%s\n" "$string"
while IFS= read -r choice; do
case $choice in
"$s1"|"$s2" ) printf 'A first choice %-20s' "$choice"; ;;&
$string ) printf 'String choice %-20s' "$choice"; ;;&
$s1|$s2 ) printf 'Two val choice %-20s' "$choice"; ;;
*) printf 'A Bad choice! %-20s' "$choice"; ;;
esac
echo
done <<-\_several_strings_
f
b
foo
bar
*foo*
*foo*|*bar*
\"foo\"
"foo"
afooline
onebarvalue
now foo with spaces
_several_strings_
答案2
您可以使用以下extglob
选项:
shopt -s extglob
string='@(foo|bar)'
答案3
您需要两个变量,case
因为 or|
管道已被解析前模式得到扩展。
v1=foo v2=bar
case foo in ("$v1"|"$v2") echo foo; esac
foo
变量中的 Shell 模式在带引号或不带引号时的处理方式也不同:
q=?
case a in
("$q") echo question mark;;
($q) echo not a question mark
esac
not a question mark
答案4
可以使用AWK的语法
if($field ~ /regex/)
也
if($i ~ var)
将变量与输入进行比较(var 和星形参数列表$*
)
parse_arg_exists() {
[ $# -eq 1 ] && return
[ $# -lt 2 ] && printf "%s\n" "Usage: ${FUNCNAME[*]} <match_case> list-or-\$*" \
"Prints the argument index that's matched in the regex-case (~ patn|patn2)" && exit 1
export arg_case=$1
shift
echo "$@" | awk 'BEGIN{FS=" "; ORS=" "; split(ENVIRON["arg_case"], a, "|")} {
n=-1
for(i in a) {
for(f=1; f<=NF; f++) {
if($f ~ a[i]) n=f
}
}
}
END {
if(n >= 0) print "arg index " n "\n"
}'
unset arg_case
}
string="--dot|-d"
printf "testing %s\n" "$string"
args="--dot -b -c"; printf "%s\n" "$args"
parse_arg_exists "$string" "$args"
args="-b -o"; printf "%s\n" "$args"
parse_arg_exists "$string" "$args"
args="-b -d -a"; printf "%s\n" "$args"
parse_arg_exists "$string" "$args"
打印出:
testing --dot|-d
--dot -b -c
arg index 1
-b -o
-b -d -a
arg index 2