【问题标题】:Bash If-statement to check If string is equal to one of several string literals [duplicate]Bash If-statement 检查字符串是否等于几个字符串文字之一[重复]
【发布时间】:2014-03-07 19:42:59
【问题描述】:

在我的 bash 脚本中,我使用以下 if 语句检查第一个参数是 -0+

LEVEL=$1
if [ "$LEVEL" -ne "-" ] && [ "$LEVEL" -ne "0" ] && [ "$LEVEL" -ne "+" ]
then
     echo "The value of LEVEL must be either -, 0, or +!"
     exit 1
fi

但它给了我错误[: -: integer expression expected,指的是带有if 语句条件的行。

我一直在尝试许多不同的语法(例如,双括号与单括号,引用与不引用变量和字符串文字),但我无法弄清楚。

【问题讨论】:

  • man test-ne/-eq 仅用于数字。
  • 仅供参考,对于环境变量和内置变量,使用全大写变量名称是常规的——而不是未导出的本地变量,如这里。遵循该规则可以避免错误地覆盖环境变量。
  • 我相信这应该作为stackoverflow.com/questions/21157435/…的副本关闭。另一个问题比另一个问题表述得更清楚,来得更快,得票更多,答案也更好。我无法明确投票结束,因为另一个问题目前已关闭。

标签: bash


【解决方案1】:

与 POSIX sh 兼容而不是利用 bashism 的传统方法是使用 case 语句:

case $level in
   -|0|+)
     echo "Got it!" ;;
   *)
     echo "Not a valid value" ;;
esac

也就是说,如果你想使用test,你也可以这样做:

if [ "$LEVEL" != "-" ] && [ "$LEVEL" != "0" ] && [ "$LEVEL" != "+" ]; then
  ...
fi

!= 是否定字符串比较运算符,= (not ==) 是 POSIX 兼容的正匹配运算符。 (Bash 扩展了 POSIX 以在测试中支持 ==,但是如果您以后尝试为纯 POSIX shell 编写代码,养成使用此扩展的习惯会给您带来麻烦)。


在这里,有bashisms! (以上符合 POSIX)。


在评论中,提出了一个后续问题,即是否可以从变量中读取可能的字符集。一般来说,是的,但有一些警告:

# no spaces in possible_levels, as we're using it to form a pattern
possible_levels='-0+'
possible_levels_pattern="[${possible_levels}]"
if [[ $value = $possible_levels_pattern ]]; then
  echo "value contains a valid level"
fi

...作为一种更短的方法,它允许您的输入字符串不加修改地使用,并且几乎是正确的:

# caveat: will say some incorrect strings, like "- 0" or "0 +", are valid levels
possible_levels=' - 0 +'
check_level() {
  [[ " $possible_levels " = *" $1 "* ]] && echo "Value is a valid level"
}

...或者,作为另一种实现(冗长,但细节正确):

possible_levels=' - 0 +'
read -a possible_levels_array <<<"$possible_levels"
check_level() {
  local possible_level
  local value=$1
  for possible_level in "${possible_levels_array[@]}"; do
    [[ $value = "$possible_level" ]] && return 0
  done
  return 1
}

if check_level "$value"; then
  echo "$value is a valid level"
else
  echo "$value is not a valid level"
fi

显然,这是一项繁重的工作,而且一般来说,只要在适当的时候(并且可能在不损失安全性的情况下)对比较进行硬编码,就可以省去试图让事情变得比实际需要的更通用的麻烦。

现在,如果我们可以将变量作为关联数组传入,而不需要支持愚蠢的空格分隔列表,那么它会更短:

declare -A possible_levels=( [-]=1 [+]=1 [0]=1 )
if [[ ${possible_levels[$value]} ]]; then
  echo "valid level"
else
  echo "invalid level"
fi

【讨论】:

  • 假设我有一个变量possiblelevels="- 0 +",我可以在case 语句中以某种方式使用变量$possiblelevels 来检查$level 变量吗?
  • @synaptik,我已经适当地跟进了。不在 case 语句中,并且使用这样的空格,您也会将空格字符视为有效级别,这可能不是您想要的。
  • 非常感谢。您的回复很有帮助。
  • @synaptik,很高兴为您提供帮助。我添加了一些进一步的编辑,包括支持您建议的以空格分隔的确切级别列表。 (使用具有有效值的关联数组作为检查键会更容易/更少代码,但这假设控制列表的格式)。
【解决方案2】:

您可以使用[[ and ]] 使用选择器:

if [[ "$LEVEL" == [0+-] ]]; then
  echo "got it"
fi

【讨论】:

  • +1 -- 很好地使用了 bashisms,虽然我倾向于在这里使用 = 而不是 == 以避免构建手指记忆,这会迫使人们编写没有意义的非 POSIX 代码到(而[[ ]] 是一种不兼容,实际上增加了价值,=== 只是糖)
  • @CharlesDuffy 是的,甚至没有糖。我已经用 C/C++ 编程了 20 多年,甚至我认为 == 在 shell 脚本中是难看的。我不知道为什么 bash 的人认为它会取悦 C 程序员。在 C 中,== 只是一个必要的邪恶,因为你可以在任何地方执行=(赋值),包括内部条件表达式。糟糕的是,我必须整天在我的 C 代码中输入==。我到底为什么要把这种丑陋和我一起拖到我使用的所有其他语言中?
  • 另外,说到这里的bashism,只要我们使用[[ ]],就不需要在$LEVEL周围加上引号。
  • [[ $LEVEL == [0+-] ]] && echo "知道了" ||回声“错误的选择”
  • @philcolbourn, ...另外,如果启用了set -e 或使用了ERR 陷阱,这很重要:使用if 语句,那么echos 都将被考虑未经检查的事件,因此如果 set -e 或 ERR 陷阱行为失败,它们将被激活;使用伪三元习语,bar 被视为“已检查”,因此如果失败,ERR 陷阱将不会激活。
猜你喜欢
  • 2022-10-13
  • 1970-01-01
  • 2014-07-29
  • 1970-01-01
  • 2017-05-24
  • 2022-06-19
  • 2012-09-21
  • 1970-01-01
  • 2020-07-16
相关资源
最近更新 更多