我正在尝试用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
,而我希望它失败。
答案 0 :(得分:133)
[[ $list =~ (^|[[:space:]])$x($|[[:space:]]) ]] && echo 'yes' || echo 'no'
或创建一个函数:
contains() {
[[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]] && exit(0) || exit(1)
}
使用它:
contains aList anItem
echo $? # 0: match, 1: failed
答案 1 :(得分:28)
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"
或
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"
答案 2 :(得分:14)
如果使用双括号,也可以在case语句之外使用(*通配符):
string='My string';
if [[ $string == *My* ]]
then
echo "It's there!";
fi
答案 3 :(得分:11)
恕我直言最简单的解决方案是在前面添加原始字符串并附加空格并使用[[ ]]
检查正则表达式
haystack='foo bar'
needle='bar'
if [[ " $haystack " =~ .*\ $needle\ .* ]]; then
...
fi
对于包含针作为子字符串的值的值,这不会是误报,例如与大海捞foo barbaz
。
(这个概念是从JQuery的hasClass()
- 方法中无耻地偷走的
答案 4 :(得分:10)
怎么样
echo $list|grep $x
你可以检查输出或上面一行的$?
来做出决定。
答案 5 :(得分:6)
如果列表已在脚本中修复,我最喜欢以下内容:
validate() {
grep -F -q -x "$1" <<EOF
item 1
item 2
item 3
EOF
}
然后使用validate "$x"
来测试是否允许$x
。
如果你想要一个单行,并且不关心项目名称中的空白,你可以使用它(注意-w
而不是-x
):
validate() { echo "11 22 33" | grep -F -q -w "$1"; }
注意:
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
答案 6 :(得分:5)
如果要在脚本中对您的值列表进行硬编码,则使用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
如果列表是运行时的数组变量,则其他答案之一可能更适合。
答案 7 :(得分: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
这适用于以空格分隔的列表,但您可以通过执行以下操作将其调整为任何其他分隔符(如:
):
LIST="ITEM1:ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | sed 's|:|\\n|g' | grep -e \"^$VALUE`$\"`" ]; then
...
fi
请注意,"
是测试工作所必需的。
答案 8 :(得分:4)
考虑利用associative arrays的密钥。我认为这优于正则表达式/模式匹配和循环,虽然我没有对它进行分析。
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
输出:
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 )
答案 9 :(得分:3)
如果时间不长;您可以像这样通过逻辑或比较将它们放在相等之间。
if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
echo In the list
fi
我遇到了这个确切的问题,尽管上述情况很难看,但比起其他广义解决方案,发生了什么事更明显。
答案 10 :(得分:1)
假设TARGET变量只能是&#39;二项式&#39;或者&#39;回归&#39;然后关注:
# 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),或使用正则表达式检查更复杂的场景。
答案 11 :(得分:1)
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
答案 12 :(得分:0)
应该将解决方案添加到列表中。
# 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
答案 13 :(得分:0)
这几乎是您最初的建议,但几乎是1排。并没有其他有效答案那么复杂,而且也不取决于bash版本(可以与旧的bash一起使用)。
OK=0 ; MP_FLAVOURS="vanilla lemon hazelnut straciatella"
for FLAV in $MP_FLAVOURS ; do [ $FLAV == $FLAVOR ] && { OK=1 ; break; } ; done
[ $OK -eq 0 ] && { echo "$FLAVOR not a valid value ($MP_FLAVOURS)" ; exit 1 ; }
我想我的建议在长度和样式上都可以改进。
答案 14 :(得分:0)
受接受的响应启发的替代解决方案,但是使用了倒置逻辑:
MODE="${1}"
echo "<${MODE}>"
[[ "${MODE}" =~ ^(preview|live|both)$ ]] && echo "OK" || echo "Uh?"
在此,输入($ MODE)必须是正则表达式(“ preview”,“ live”或“ both”)中的选项之一,这与将整个选项列表与用户输入进行匹配相反。当然,您不希望正则表达式会发生变化。
答案 15 :(得分:0)
先前的答案不使用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
答案 16 :(得分:0)
演出迟到了?尚未明确提及以下非常简单的变体。我使用 case
来检查简单列表,这是一个通用的 Bourne Shell 习语,不依赖任何外部或扩展:
haystack='a b c'
needle='b'
case " $haystack " in (*" $needle "*) :;; (*) false;; esac
SPC
)来正确地分隔模式:在 " $haystack "
的开头和结尾以及 " $needle "
的测试中也是如此。立>
true
在 $?=0
中,此语句返回 $needle
($haystack
),否则返回 false。$needle
。当有几个类似的情况比如if (haystack.contains(needle1)) { run1() } elif (haystack.contains(needle2)) { run2() } else { run3() }
case
中:case " $haystack " in (*" $needle1 "*) run1;; (*" $needle2 "*) run2;; (*) run3;; esac
这也适用于所有值不包含分隔符本身的列表,例如逗号:
haystack=' a , b , c '
needle=' b '
case ",$haystack," in (*",$needle,"*) :;; (*) false;; esac
请注意,如果值可以包含任何内容,包括分隔符序列(NUL
除外,因为 shell 不支持变量中的 NUL
,因为您无法将包含 NUL
的参数传递给命令),那么您需要使用数组。数组是 ksh/bashisms,不受“普通”POSIX/Bourne shell 支持。 (您可以在 POSIX-Shells 中使用 $@
解决此限制,但这与此处提出的完全不同。)
可以省略 (*) false
部分吗?
case
返回 true
。:
的位置为什么:;;
true;;
,但我习惯使用 :
而不是 true
,因为它更短而且输入速度更快case
的默认返回值是否为真对每个人来说并不明显。在 bash
中,您还可以使用 ;&
(fallthroug)或 ;;&
(测试其他模式)等 ksh/bashisms 来表达 if (haystack.contains(needle1)) { run1(); }; if (haystack.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
的明确部分,以防您的分隔符是空字符串,对吗?
请注意,此功能属于公共领域,因为它绝对没有真正受版权保护的内容。