在Bash中检查一个字符串是否存在于一个字符串数组中

10

我知道如何在bash中比较两个字符串:

if [ "$build_type" = "devite" ]; then
  echo "building'"
fi

但我需要检查"$build_type"是否在["devite", "relite"]中。

所以类似于以下内容:

if [ "$build_type" in ["devite", "relite"] ]; then
  echo "building'"
fi

有人能解释一下这个吗?


1
bash 没有列表。最接近的类比是检查一个字符串是否是给定关联数组中的键。 - chepner
@chepner,我该如何像这样实现:如果"$build_type"="dev"或"$build_type"=rel? - Learner
同意,不要这样做。如果你碰巧有一个bash数组变量,并且想知道它是否包含特定的文本,我会使用printf将其扩展为字符串,然后进行检查。 - Gem Taylor
类似于:https://dev59.com/3HA65IYBdhLWcg3wrgsa - Efren
4个回答

13

使用 || 连接两个 test/[ 命令:

if [ "$build_type" = devite ] || [ "$build_type" = relite ]; then
  echo "building"
fi

或者使用case语句。

case $build_type in
  devite|relite) echo "building" ;;
esac

如果目标在关联数组中,您可以检查键是否存在。

declare -A targets=([devite]= [relite]=)

if [[ -v targets[$build_type] ]]; then
    echo "building"
fi

我建议在变量使用时始终使用双引号 ("$ build_type" 而不是 $ build_type)。谁知道该变量中有什么,也许有空格?那样的话,在 case 中会产生奇怪的错误。 - Alfe
1
数组索引使用的扩展不受路径名扩展或单词分割的影响。 - chepner
但是在case的情况下,这是必要的(case $build_type in),不是吗?编辑:似乎不需要,有趣。对我来说,这听起来像一个奇怪的例外...我仍然建议使用它们,因为记住所有不同的情况可能会出错,最终可能会在另一种情况下留下它们是必要的,而它们从来没有错。对我来说,没有双引号的用法已经像是“我明确地想要在这种情况下分割单词”。 - Alfe
2
作为特殊的复合语句,“case”不必遵循评估简单命令的通常规则。由于目的是比较“$build_type”的值与语句内的模式,因此在关键字“case”之后执行参数扩展时既不需要执行单词分割也不需要执行路径名扩展,这是你需要引号的唯一原因。 - chepner
是的,我理解这个想法。我仍然会始终使用双引号,除非我真的需要单词分割。但当然这更多是个人观点。 - Alfe

7
我会使用一个switch语句来处理这个问题,嗯,switch语句:
case "$build_type" in
  devite|relite)
    echo "building"
    ;;
esac

管道符号 (|) 表示“或”逻辑。

当然,使用 shell 内置的 case 命令可以做更多的事情,比如为不同的情况指定处理代码。但对于固定列表中的简单字符串(在字符串中包含特殊字符可能会变成引用噩梦),它是最容易阅读的东西。

如果您真的想使用列表数组,我建议使用循环:

names=( devite relite )
for name in "${names[@]}"
do
  if [ "$build_type" = "$name" ]
  then
    echo "building"
    break  # leave the loop
  fi
done

3
这取决于目标字符串的存储方式。如果它们存储在一个数组中(例如 a=(devite relite)),你可以这样操作:
if [[ "${a[@]}" =~ $build_type ]]; then ...

2
这也很脆弱。如果 build_type 类似于 ite rel,则会匹配。 - chepner

1
另一个选择是使用数组和grep
#!/bin/bash
declare -a types=("devite" "relite")
type=devite
neg=dovite

if grep -q "${type}" <<< "${types[*]}" ; then
        echo "building"
else
        echo "not building"
fi

if grep -q "${neg}" <<< "${types[*]}" ; then
        echo "building"
else
        echo "not building"
fi

=> building
   not building

我有很多种方法来解决这个问题 :)

编辑:如果您不能信任输入的有效性:

#!/bin/bash
declare -a types=("devite" "relite")
type=devite
neg="ite rel"

function j { local IFS=$'\n'; echo "$*"; }

if grep -q "${type}" <<< $(j "${types[@]}") ; then
        echo "building"
else
        echo "not building"
fi
if grep -q "${neg}" <<< $(j "${types[@]}") ; then
        echo "building"
else
        echo "not building"
fi

2
类似 ite rel 的类型会导致误报。 - chepner
@chepner 嗯,在现实场景中不太可能发生,我想 - 并且可以通过相应地修改IFS来避免(请参见编辑)- 但无论如何,这只是指向可能方向的一个指针。 - Tom Regner
1
为什么要绕这些弯路,当有更高效的解决方案(即不运行外部程序)可以避免这种情况呢? - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接