我试图在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'),而不是将整个选项列表匹配到用户输入。当然,您不会期望正则表达式发生变化。

其他回答

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

MODE="${1}"

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

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

例子

$ in_list super test me out
NO

$ in_list "super dude" test me out
NO

$ in_list "super dude" test me "super dude"
YES

# How to use in another script
if [ $(in_list $1 OPTION1 OPTION2) == "NO" ]
then
  echo "UNKNOWN type for param 1: Should be OPTION1 or OPTION2"
  exit;
fi

in_list

function show_help()
{
  IT=$(CAT <<EOF

  usage: SEARCH_FOR {ITEM1} {ITEM2} {ITEM3} ...

  e.g. 

  a b c d                    -> NO
  a b a d                    -> YES
  "test me" how "test me"    -> YES

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

if [ "$#" -eq 0 ]; then
  show_help
fi

SEARCH_FOR=$1
shift;

for ITEM in "$@"
do
  if [ "$SEARCH_FOR" == "$ITEM" ]
  then
    echo "YES"
    exit;
  fi
done

echo "NO"

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),或者使用正则表达式检查更复杂的场景。

之前的答案没有使用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