我试图在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,而我希望它失败。


当前回答

如果你使用双括号,你也可以在case语句外使用(*通配符):

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
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

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

考虑利用关联数组的键。我认为这优于正则表达式/模式匹配和循环,尽管我还没有对其进行分析。

declare -A list=( [one]=1 [two]=two [three]='any non-empty value' )
for value in one two three four
do
    echo -n "$value is "
    # a missing key expands to the null string, 
    # and we've set each interesting key to a non-empty value
    [[ -z "${list[$value]}" ]] && echo -n '*not* '
    echo "a member of ( ${!list[*]} )"
done

输出:

1是(1,2,3)的成员 2是(1,2,3)的一个元素 3是(1,2,3)的一个元素 4不是(1,2,3)的成员

之前的答案没有使用tr,我发现它对grep很有用。假设列表中的项以空格分隔,检查精确匹配:

echo $mylist | tr ' ' '\n' | grep -F -x -q "$myitem"

如果条目在列表中,这将返回退出代码0,如果不在列表中,则返回退出代码1。

最好将它作为函数使用:

_contains () {  # Check if space-separated list $1 contains line $2
  echo "$1" | tr ' ' '\n' | grep -F -x -q "$2"
}

mylist="aa bb cc"

# Positive check
if _contains "${mylist}" "${myitem}"; then
  echo "in list"
fi

# Negative check
if ! _contains "${mylist}" "${myitem}"; then
  echo "not in list"
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的清晰部分如果分隔符是空字符串,对吧?

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

在我看来,最简单的解决方案是在原始字符串前加上一个空格,并使用[[]]检查正则表达式。

haystack='foo bar'
needle='bar'

if [[ " $haystack " =~ .*\ $needle\ .* ]]; then
    ...
fi

对于包含needle作为子字符串的值,这将不会是假阳性,例如用haystack foo barbaz。

(这个概念是从JQuery的hasClass()-Method中偷来的)