如何检查BASH列表中是否存在变量

Ofi*_*chy 123 linux bash

我试图用bash编写一个脚本来检查用户输入的有效性.
我想将输入(比如变量x)与有效值列表进行匹配.

我现在想出的是:

for item in $list
do
    if [ "$x" == "$item" ]; then
        echo "In the list"
        exit
    fi
done
Run Code Online (Sandbox Code Playgroud)

我的问题是,是否有一种更简单的方法可以做到这一点,
就像list.contains(x)大多数编程语言一样.

增加:
说清单是:

list="11 22 33"
Run Code Online (Sandbox Code Playgroud)

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

che*_*ila 137

[[ $list =~ (^|[[:space:]])$x($|[[:space:]]) ]] && echo 'yes' || echo 'no'
Run Code Online (Sandbox Code Playgroud)

或创建一个功能:

contains() {
    [[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]] && exit(0) || exit(1)
}
Run Code Online (Sandbox Code Playgroud)

使用它:

contains aList anItem
echo $? # 0? match, 1: failed
Run Code Online (Sandbox Code Playgroud)

  • 应该是`[[$ list =〜(^ |)$ x($ |)]] && echo'yes'|| 回声'不' (33认同)
  • 如果用户输入包含正则表达式特殊字符,例如`x = .`,则可能会出现误报 (12认同)
  • 一个更简洁的解决方案:`[["$ list"=〜"$ x"]] && echo'yes'|| 回声'不'.这是正确的,假设空间是分隔符,而'$ x`不包含空格 (6认同)
  • 这不会给出误报/否定:`contains(){[["$ 1"=〜(^ | [[:space:]])"$ 2"($ | [[:space:]])]]; }`. (3认同)
  • 我认为最好使用“is in”函数`isIn()`,这样你就可以先在参数中写入项目。你也可以`echo`一些东西而不是像这样使用`exit`:`[[ $2 =~ (^|[[:space:]])$1($|[[:space:]]) ]] && echo 1 || echo 0` 所以你可以这样使用这个函数:`result=$(isIn "-t" "-o -t 45") && echo $result` (2认同)
  • 答案就是错误的;意外标记附近出现语法错误。@skozin 的版本始终返回 1,即使该字符串不在列表中。 (2认同)
  • `scripts/1.sh: 第 5 行:意外标记 \`0' 附近出现语法错误 ` `scripts/1.sh: 第 5 行:\` [[ $1 =~ (^|[[:space:]])$2( $|[[:空格:]]) ]] && 退出(0) || 退出(1)'` (2认同)

Ori*_*axx 32

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

[[ $list =~ (^|[[:space:]])"$x"($|[[:space:]]) ]] && echo 'yes' || echo 'no' 
Run Code Online (Sandbox Code Playgroud)

所以,即

# 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
}
Run Code Online (Sandbox Code Playgroud)

那么结束

`list_include_item "10 11 12" "12"`  && echo "yes" || echo "no"
Run Code Online (Sandbox Code Playgroud)

要么

if `list_include_item "10 11 12" "1"` ; then
  echo "yes"
else 
  echo "no"
fi
Run Code Online (Sandbox Code Playgroud)

请注意,必须使用""变量:

`list_include_item "$my_list" "$my_item"`  && echo "yes" || echo "no"
Run Code Online (Sandbox Code Playgroud)

  • 即使`$ item`包含特殊字符,例如`.`,这个解决方案仍然有效(但是`$ list`变量可能需要在测试中引用).并且函数可以更简单地定义:`contains(){[["$ 1"=〜(^ | [[:space:]])"$ 2"($ | [[:space:]])]]; }`. (3认同)

Ami*_*n3t 26

有一种更简洁的方法来检查字符串是否在列表中:

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

  • 我以前也从未见过这个。它记录在此处:https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching。_"@(pattern-list): 匹配给定模式之一。"_ 这还需要启用 `extglob` shell 选项:_"如果使用内置 shopt 启用 extglob shell 选项,则 shell 会识别多个扩展模式匹配运算符。”_列出了一些类似的运算符,它们也很有趣。 (3认同)
  • 你如何将它与数组一起使用?目前感觉像是一个不完整的答案。 (2认同)

小智 17

恕我直言最简单的解决方案是在前面添加原始字符串并附加空格并检查正则表达式 [[ ]]

haystack='foo bar'
needle='bar'

if [[ " $haystack " =~ .*\ $needle\ .* ]]; then
    ...
fi
Run Code Online (Sandbox Code Playgroud)

对于包含针作为子串的值的值,这不会是误报,例如使用大海捞针foo barbaz.

(这个概念是由JQuery的hasClass()-Method 无耻地窃取的)

  • 如果分隔符不是空格,则解决方案更明显.它也可以在没有正则表达式的情况下完成:`haystack ="foo:bar"`和`[[":$ haystack:"=*:$ needle:*]]` (2认同)
  • 如果您使用=~并引用needle,则不需要任何通配符(无论分隔符如何):`[[“$haystack”=~“$needle”]]`(请参阅[此答案类似问题](/sf/answers/1077631691/))。 (2认同)

Mit*_*ran 15

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

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
fi
Run Code Online (Sandbox Code Playgroud)

  • 如果"My"是另一个字符串的子字符串,例如string ='MyCommand string',这将给出误报. (13认同)
  • 简约优雅,+ 1 (3认同)
  • 值得注意的是,通配符 (`*My*`) 必须在测试的右侧。 (2认同)

Ken*_*ent 13

怎么样

echo $list | grep -w $x
Run Code Online (Sandbox Code Playgroud)

您可以检查输出或 $?上面的行来做出决定.

  • 如果“value”有效(即在列表中),那么这不会验证“val”,因为它是它的子字符串 (3认同)
  • @carnicer然后只需使用grep -w $ x即可完全匹配 (3认同)
  • 你可以使用grep -q使grep变得安静 (2认同)

小智 11

如果不是太长;您可以像这样沿着逻辑 OR 比较将它们在相等之间串起来。

if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
    echo In the list
fi 
Run Code Online (Sandbox Code Playgroud)

我遇到了这个确切的问题,虽然上面的内容很丑陋,但与其他通用解决方案相比,发生的事情更明显。


Rub*_*ONO 7

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

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
Run Code Online (Sandbox Code Playgroud)

输出:

one is a member of ( one two three )
two is a member of ( one two three )
three is a member of ( one two three )
four is *not* a member of ( one two three )
Run Code Online (Sandbox Code Playgroud)

  • ...您可以通过创造性地使用参数来简化替换(而不是依赖于 `echo -n`):`do is="${list[$value]+is }"; echo "$value ${is:-is *not* }a member of ( ${!list[*]} )"; 完成`。 (2认同)

Sør*_*org 7

如果在脚本中修复了列表,我最喜欢以下内容:

validate() {
    grep -F -q -x "$1" <<EOF
item 1
item 2
item 3
EOF
}
Run Code Online (Sandbox Code Playgroud)

然后validate "$x"用来测试是否$x允许.

如果你想要一个单行,并且不关心项目名称中的空格,你可以使用它(注意-w而不是-x):

validate() { echo "11 22 33" | grep -F -q -w "$1"; }
Run Code Online (Sandbox Code Playgroud)

笔记:

  • sh符合POSIX 标准.
  • validate没有接受的子串(删除-x选项给grep如果你想要的).
  • validate将其参数解释为固定字符串,而不是正则表达式(-F如果需要,请删除grep选项).

练习函数的示例代码:

for x in "item 1" "item2" "item 3" "3" "*"; do
    echo -n "'$x' is "
    validate "$x" && echo "valid" || echo "invalid"
done
Run Code Online (Sandbox Code Playgroud)


Tob*_*ght 7

如果要在脚本中对值列表进行硬编码,则使用进行测试相当简单case。这是一个简短的示例,您可以根据需要进行调整:

for item in $list
do
    case "$x" in
      item1|item2)
        echo "In the list"
        ;;
      not_an_item)
        echo "Error" >&2
        exit 1
        ;;
    esac
done
Run Code Online (Sandbox Code Playgroud)

如果列表在运行时是数组变量,则其他答案之一可能更合适。


Séb*_*rre 5

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

LIST="ITEM1 ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | xargs -n1 echo | grep -e \"^$VALUE`$\" ]; then
    ...
fi
Run Code Online (Sandbox Code Playgroud)

这适用于以空格分隔的列表,但您可以:通过执行以下操作使其适应任何其他分隔符(如):

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

请注意,"测试工作是必需的.


Sam*_*mWN 5

我想我会将我的解决方案添加到列表中。

# Checks if element "$1" is in array "$2"
# @NOTE:
#   Be sure that array is passed in the form:
#       "${ARR[@]}"
elementIn () {
    # shopt -s nocasematch # Can be useful to disable case-matching
    local e
    for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
    return 1
}

# Usage:
list=(11 22 33)
item=22

if elementIn "$item" "${list[@]}"; then
    echo TRUE;
else
    echo FALSE
fi
# TRUE

item=44
elementIn $item "${list[@]}" && echo TRUE || echo FALSE
# FALSE
Run Code Online (Sandbox Code Playgroud)


Qub*_*b3r 5

shell 内置的 compgen 可以在这方面提供帮助。它可以获取带有 -W 标志的列表并返回它找到的任何潜在匹配项。

# My list can contain spaces so I want to set the internal
# file separator to newline to preserve the original strings.
IFS=$'\n'

# Create a list of acceptable strings.
accept=( 'foo' 'bar' 'foo bar' )

# The string we will check
word='foo'

# compgen will return a list of possible matches of the 
# variable 'word' with the best match being first.
compgen -W "${accept[*]}" "$word"

# Returns:
# foo
# foo bar
Run Code Online (Sandbox Code Playgroud)

我们可以编写一个函数来测试字符串是否等于可接受字符串的最佳匹配。这允许您分别返回 0 或 1 表示通过或失败。

function validate {
  local IFS=$'\n'
  local accept=( 'foo' 'bar' 'foo bar' )
  if [ "$1" == "$(compgen -W "${accept[*]}" "$1" | head -1)" ] ; then
    return 0
  else
    return 1
  fi
}
Run Code Online (Sandbox Code Playgroud)

现在您可以编写非常干净的测试来验证字符串是否可接受。

validate "blah" || echo unacceptable

if validate "foo" ; then
  echo acceptable
else 
  echo unacceptable
fi
Run Code Online (Sandbox Code Playgroud)