我想要如下内容:
#!/bin/sh
# ... other stuff ...
# some relatively static possibilities (srsp):
srsp='this|or|that|the|other'
# more other stuff
case $something in
$srsp) # <- problem is here
do_something # or maybe nothing
;;
this|or|that|the|other);; # this would work, but loses the benefit of a variable
*)
# anything not in my list is an error:
echo "Sorry, I don't recognize $something as one of $srsp" >&2
exit 1;;
esac
do_something | egrep "blah($srsp)thing" # or whatever
问题是,$srsp
那里只匹配整个字符串(如果$something
恰好是字符串"this|or|that|or|some|other|stuff"
,它会匹配并调用do_something
),而不是任何this
或or
或that
......等等:我真正想要匹配的值。
如果我把这个文字字符串放在case语句中(我的“这会起作用”行),它会匹配我想要匹配的内容,但我试图保持这个干燥,并且我需要在稍后使用的正则表达式中使用相同的字符串集。(旁注:我知道匹配中可能出现的情况case
和正则表达式中可能出现的情况可能会有很大差异,但在我的特定情况下,它们与两者都兼容。实际上,各个组件中只有字母,没有通配符,只有|
,它在两个系统中都作为特殊情况存在。)
那么,有办法吗?特别是不将整个 case 语句包装在 eval 或其他东西中?我仍然想将其保留在 case 语句中,因为我还有其他事情要做。(我确信我可以通过重新构造并使用 egrep 作为我的匹配测试(if echo $something | egrep "^($srsp)$" > /dev/null
或类似的东西)来实现一种解决方法。这个问题是关于试图找到一种不必诉诸于此的方法。或者明确知道它无法完成也是一个有效的答案。)
(或者我应该切换到 common-lisp? ;))
sh
对于我的需求,我肯定会满意bash
,并且可能 zsh
,但如果有一种方法可以以最大程度可移植的方式(即sh
)做到这一点,那么在我看来,这将是一个更好的答案。
答案1
这实际上与本身无关case
:您需要一种机制来检查列表中是否存在某个单词。使用 bash 您可以这样做:
srsp=(this that or the other)
in_srsp() {
# this joins the array into a string with spaces (default value of IFS)
# then tests if the space-delimited word is a match for that string.
[[ " ${srsp[*]} " == *" $1 "* ]]
}
something=foo
if in_srsp $something; then echo y; else echo n; fi # => n
something=other
if in_srsp $something; then echo y; else echo n; fi # => y
更便携,只需翻转外壳即可
srsp="this|that|or|the|other"
something=other
case "|$srsp|" in
*"|$something|"*) echo in;;
*) echo not;;
esac
答案2
eval
当然,您可以使用,但它相当笨重。也许可以将其封装到函数中,这样您就可以避免在实际复杂的之外使用太多反斜杠eval
。
in_list () {
local var
var=$1
local list
list=$2
eval case \$var in "$list"\) return 0\;\; \*) return 1\;\; esac
}
in_list "foo" "foo|bar|baz" && echo Success
in_list "nonesvch" "foo|bar|baz" || echo More so
如果你知道你可以限制列表中的字符集,也许可以在安全case
之前运行过滤eval
,例如
in_list () {
local var
var=$1
local list
list=$2
case $var in *[!-a-z0-9_]*) return 1;; esac
# Additionally, allow wildcards and whitespace in list
case $list in *[!?*| a-z0-9_-]*)
echo "$0: Invalid characters in $list" >&2; return 2;;
esac
eval case \$var in "$list"\) return 0\;\; \*) return 1\;\; esac
}