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


当前回答

我想把我的解决方案也加进去。

# 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

其他回答

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

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)的成员

下面的脚本实现了一个列表的包含函数。

    function contains {
      local target=$1
      shift
    
      printf '%s\n' "$@" | grep -x -q "$target"
      out=$?
      (( out = 1 - out ))
      return $out
    }

如果将一个基于空白的字符串转换为一个列表并使用它,它似乎可以按以下方式解决。


    list="11 22 33"
    IFS=" " read -ra parsed_list <<< "$list"
    
    # parsed_list would be ("11" "22" "33")
    
    contains "11" "${parsed_list[@]}"
    echo $?  # 1
    
    contains "22" "${parsed_list[@]}"
    echo $?  # 1
    
    contains "1" "${parsed_list[@]}"
    echo $? # 0
    
    contains "11 22" "${parsed_list[@]}"
    echo $? # 0

如果将列表固定在脚本中,我最喜欢以下几个:

validate() {
    grep -F -q -x "$1" <<EOF
item 1
item 2
item 3
EOF
}

然后使用验证“$x”来测试是否允许使用$x。

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

validate() { echo "11 22 33" | grep -F -q -w "$1"; }

注:

这是POSIX sh兼容的。 Validate不接受子字符串(如果需要,可以删除grep的-x选项)。 Validate将其参数解释为固定字符串,而不是常规字符串 表达式(如果需要,可以删除grep的-F选项)。

演示函数的示例代码:

for x in "item 1" "item2" "item 3" "3" "*"; do
    echo -n "'$x' is "
    validate "$x" && echo "valid" || echo "invalid"
done

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

我们可以编写一个函数来测试字符串是否等于可接受字符串的最佳匹配。这允许您分别为通过或失败返回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
}

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

validate "blah" || echo unacceptable

if validate "foo" ; then
  echo acceptable
else 
  echo unacceptable
fi

假设TARGET变量只能是“二项式”或“回归”,那么如下所示:

# Check for modeling types known to this script
if [ $( echo "${TARGET}" | egrep -c "^(binomial|regression)$" ) -eq 0 ]; then
    echo "This scoring program can only handle 'binomial' and 'regression' methods now." >&2
    usage
fi

您可以通过使用|(管道)字符分隔更多字符串到列表中。

使用egrep的优点是可以轻松地添加大小写不敏感(-i),或者使用正则表达式检查更复杂的场景。