我试图在bash中编写一个脚本,检查用户输入的有效性。 我想将输入(变量x)匹配到一个有效值列表。

我现在想到的是:

for item in $list
do
    if [ "$x" == "$item" ]; then
        echo "In the list"
        exit
    fi
done

我的问题是,如果有更简单的方法, 对于大多数编程语言,类似list.contains(x)。

列表是:

list="11 22 33"

我的代码将只对这些值回显消息,因为list被视为数组而不是字符串, 所有的字符串操作都将验证1,而我希望它失败。


当前回答

另一种受公认回答启发的解决方案,但使用了相反的逻辑:

MODE="${1}"

echo "<${MODE}>"
[[ "${MODE}" =~ ^(preview|live|both)$ ]] && echo "OK" || echo "Uh?"

这里,输入($MODE)必须是正则表达式中的一个选项('preview', 'live',或'both'),而不是将整个选项列表匹配到用户输入。当然,您不会期望正则表达式发生变化。

其他回答

有一种更简单的方法来检查string是否在列表中:

if [[ $my_str = @(str1|str2|str3) ]]; then
    echo "string found"
fi

我发现使用echo $LIST | xargs -n1 echo | grep $VALUE更容易,如下图所示:

LIST="ITEM1 ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | xargs -n1 echo | grep -e \"^$VALUE`$\" ]; then
    ...
fi

这适用于空格分隔的列表,但你可以通过执行以下操作将其调整为任何其他分隔符(如:):

LIST="ITEM1:ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | sed 's|:|\\n|g' | grep -e \"^$VALUE`$\"`" ]; then
   ...
fi

注意,“是测试工作所必需的。

演出迟到了?下面的非常简单的变体还没有明确提到。我用case检查简单的列表,这是一个不依赖任何外部或扩展的Bourne Shell成语:

haystack='a b c'
needle='b'

case " $haystack " in (*" $needle "*) :;; (*) false;; esac

Please note the use of the separator (here: SPC) to correcyly delimit the pattern: At the beginning and end of " $haystack " and likewise in the test of " $needle ". This statement returns true ($?=0) in case $needle is in $haystack, false otherwise. Also you can test for more than one $needle very easily. When there are several similar cases like if (haystack.contains(needle1)) { run1() } elif (haystack.contains(needle2)) { run2() } else { run3() } you can wrap this into the case, too: case " $haystack " in (*" $needle1 "*) run1;; (*" $needle2 "*) run2;; (*) run3;; esac and so on

这也适用于所有值不包括分隔符本身的列表,比如逗号:

haystack=' a , b , c '
needle=' b '

case ",$haystack," in (*",$needle,"*) :;; (*) false;; esac

请注意,如果值可以包含包括分隔符序列的任何内容(除了NUL,因为shell不支持变量中的NUL,因为您不能将包含NUL的参数传递给命令),那么您需要使用数组。数组是ksh/bashisms,不受“普通”POSIX/Bourne shell的支持。(你可以在posix - shell中使用$@来解决这个限制,但这与这里完全不同。)

错误的部分可以去掉吗?

不,因为这是临界返回值。默认情况下,case返回true。 如果你不需要返回值并将你的处理放在:

为什么:;;

我们也可以写成true;;,但我习惯用:而不是true,因为这样打字更短更快 另外,我认为不写任何不好的做法,因为对于每个人来说,case的默认返回值是true并不明显。 此外,“leaving out”这个命令通常表示“这里忘记了一些东西”。因此,在这里放一个多余的“:”清楚地表明“它的目的只是在这里返回true”。

在bash中,您还可以使用ksh/bashisms,如;& (fallthrough)或;;&(测试其他模式)来表示if (hastack .contains(needle1)) {run1();};If (hastack .contains(needle2)) {run2();}

因此,case通常比其他正则表达式结构更易于维护。此外,它不使用正则表达式,它只使用shell模式,这甚至可能更快。


可重用的功能:

: Needle "list" Seperator_opt
NeedleListSep()
{
  if [ 3 -gt $# ]; 
  then NeedleListSep "$1" "$2" " ";
  else case "$3$2$3" in (*"$3$1$3"*) return 0;; esac; return 1;
  fi;
}

在bash中,您可以将其简化为

: Needle "list" Seperator_opt
NeedleListSep()
{
  local s="${3-" "}";
  case "$s$2$s" in (*"$s$1$s"*) return 0;; esac; return 1;
}

像这样使用

Test() {
NeedleListSep "$1" "a b c"           && echo found $1 || echo no $1;
NeedleListSep "$1" "a,b,c"     ','   && echo found $1 || echo no $1;
NeedleListSep "$1" "a # b # c" ' # ' && echo found $1 || echo no $1;
NeedleListSep "$1" "abc"       ''    && echo found $1 || echo no $1;
}
Test a
Test z

如上所示,这也适用于分隔符为空字符串的退化情况(因此列表中的每个字符都是针)。例子:

Test

返回

no
no
no
found

因为空字符串是abc的清晰部分如果分隔符是空字符串,对吧?

请注意,这个函数是公共领域的,因为它绝对没有任何东西可以真正获得版权。

如果不是太长;你可以像这样用逻辑或比较把它们串在相等之间。

if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
    echo In the list
fi 

我遇到过这个问题,虽然上面的问题很难看,但它比其他广义解决方案更明显。

Matvey是对的,但你应该引用$x,并考虑任何类型的“空格”(例如新行)

[[ $list =~ (^|[[:space:]])"$x"($|[[:space:]]) ]] && echo 'yes' || echo 'no' 

所以,即。

# list_include_item "10 11 12" "2"
function list_include_item {
  local list="$1"
  local item="$2"
  if [[ $list =~ (^|[[:space:]])"$item"($|[[:space:]]) ]] ; then
    # yes, list include item
    result=0
  else
    result=1
  fi
  return $result
}

然后结束

`list_include_item "10 11 12" "12"`  && echo "yes" || echo "no"

or

if `list_include_item "10 11 12" "1"` ; then
  echo "yes"
else 
  echo "no"
fi

注意,在变量的情况下必须使用"":

`list_include_item "$my_list" "$my_item"`  && echo "yes" || echo "no"