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


137

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

Answers:


142
[[ $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

37
应该是[[ $list =~ (^| )$x($| ) ]] && echo 'yes' || echo 'no'
Matvey Aksenov

12
如果用户输入包含正则表达式特殊字符,则可能给出误报,例如x=.
glenn jackman 2011年

4
这不会产生误报/否定:contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }
2014年

6
更简洁的解决方案:[[ " $list " =~ " $x " ]] && echo 'yes' || echo 'no'。假设空间是分隔符$x且不包含空间,这是正确的
Tianren Liu

2
我认为最好使用“在”功能,isIn()以便您可以先在参数中写入该项目。您也可以echo使用exit类似的方法来代替:[[ $2 =~ (^|[[:space:]])$1($|[[:space:]]) ]] && echo 1 || echo 0所以您可以这样使用函数:result=$(isIn "-t" "-o -t 45") && echo $result
hayj

34

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"

3
即使$item包含特殊字符,此解决方案也可以使用.(例如,但该$list变量可能需要在测试中引用)。而且该函数甚至可以定义得更简单:contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }
skozin 2014年

:在同类个案不工作list="aa bb xx cc ff"x="aa bb"
creativeChips

你好。我正在尝试学习bashscript的基础知识,我想知道如何将list的字符串解析为list,然后在该列表中进行是否存在检查。我可以理解您是使用空格拆分的,但我无法完全理解该表达方式。您可以向我提供Google的关键字吗$list =~ (^|[[:space:]])"$item"($|[[:space:]])?或者,如果您有时间,我很高兴听到您对此表达的解释。注意:我猜这是一个正则表达式(以^等开头),但是我不知道=〜是什么意思。所以,我很想总体解释:P
AliYılmaz,

28

恕我直言,最简单的解决方案是在原始字符串的前面加上一个空格,并使用 [[ ]]

haystack='foo bar'
needle='bar'

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

对于包含针作为子字符串的值(例如,带有干草堆)的值,这不会是假阳性foo barbaz

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


4
如果分隔符不是空格,则解决方案更加明显。也可以不用正则表达式来完成: haystack="foo:bar"[[ ":$haystack:" = *:$needle:* ]]
phiphi '18

25

怎么样

echo $list | grep -w $x

您可以检查输出还是 $?上面的行来做出决定。

grep -w 检查整个单词模式。


1
如果“ value”有效(即在列表中),因为它是它的子字符串,因此不会验证“ val”
Ofir Farchy 2011年

是的,可以接受。@glennjackman提供了一个可行的解决方案。
f.ardelian

3
您可以使用grep -q使grep保持安静
Amir

这也将接受部分匹配,这可能不是用户想要什么
carnicer

3
@carnicer然后使用grep -w $ x进行精确匹配
user1297406

18

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

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
fi

3
古朴典雅,+1
若奥·佩雷拉

14
如果“ My”是另一个字符串的子字符串,例如string ='MyCommand string',这将给出假肯定。
路加·李

值得注意的是,通配符(*My*)必须位于测试的右侧。
Jacob Vanus

8

如果要在脚本中对值列表进行硬编码,则使用进行测试相当简单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

如果列表在脚本中是固定的,那么我最喜欢以下内容:

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符合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

6

考虑利用关联数组的键。我可能认为这比正则表达式/模式匹配和循环都要好,尽管我没有介绍它。

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 )

2
...你可以简化更换(而不是依赖echo -n)与创造性地运用的参数:do is="${list[$value]+is }"; echo "$value ${is:-is *not* }a member of ( ${!list[*]} )"; done
Toby Speight 2016年

如果我只有`list =“ one two three xyz ...”这样的(长)列表,是否有一种简单的方法来创建这样的数组?
奥特·图梅特

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

请注意,"测试才能正常运行。


LIST="SOMEITEM1 ITEM2"即使ITEM1不在其中,这也会导致返回true
Ofir Farchy

很好,我使用grep -e更新了示例,以排除部分匹配。
塞巴斯蒂安·皮埃尔

我相信在“ if”语句的末尾还有一个额外的`。正确的形式是:[-n“`echo $ LIST | xargs -n1 echo | grep -e \” ^ $ VALUE $ \“]
Elad Tabak

3

以为我会将解决方案添加到列表中。

# 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

1

例子

$ 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"

1

假设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),或使用正则表达式检查更复杂的情况。


1

这几乎是您的原始建议,但几乎是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 ; }

我想我的建议在长度和样式上都可以改进。


0

如果时间不长;您可以像这样通过逻辑或比较将它们放在相等之间。

if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
    echo In the list
fi 

我遇到了这个确切的问题,尽管上述情况很难看,但比起其他通用解决方案来说,发生的情况更为明显。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.