测试元素是否在bash中的数组中


17

有没有一种好的方法来检查数组是否在bash中有一个元素(比循环更好)?

或者,是否存在另一种方法来检查数字或字符串是否等于一组预定义常数?

Answers:


24

在Bash 4中,您可以使用关联数组:

# set up array of constants
declare -A array
for constant in foo bar baz
do
    array[$constant]=1
done

# test for existence
test1="bar"
test2="xyzzy"

if [[ ${array[$test1]} ]]; then echo "Exists"; fi    # Exists
if [[ ${array[$test2]} ]]; then echo "Exists"; fi    # doesn't

要最初设置阵列,您还可以直接分配:

array[foo]=1
array[bar]=1
# etc.

或者这样:

array=([foo]=1 [bar]=1 [baz]=1)

实际上,在值为空的情况下,[[]]测试无效。例如,“ array ['test'] =””。在这种情况下,键'test'存在,并且您可以看到它以$ {!array [@]}列出,但是“ [[$ $ array('test']}]]; echo $?”。回波1而不是0
haridsv

1
${array[$test1]}很简单,但有一个问题:如果set -u在脚本中使用(建议使用),它将不起作用,因为您会得到“未绑定变量”。
tokland 2012年

@tokland:谁推荐?我当然不会。
暂停,直到另行通知。

@DennisWilliamson:好的,有人建议这样做,但是我认为拥有一个不管这些标志的值如何都能起作用的解决方案会很好。
tokland 2012年


10

这是一个老问题,但是我认为最简单的解决方案尚未出现:test ${array[key]+_}。例:

declare -A xs=([a]=1 [b]="")
test ${xs[a]+_} && echo "a is set"
test ${xs[b]+_} && echo "b is set"
test ${xs[c]+_} && echo "c is set"

输出:

a is set
b is set

要查看此工作方式,请检查此内容


2
建议您使用该信息手册,env以避免别名,程序和其他可能采用名称“ test”的函数出现歧义。如上env test ${xs[a]+_} && echo "a is set"。您也可以使用双括号来获得此功能,然后使用相同的技巧然后检查是否为空:[[ ! -z "${xs[b]+_}" ]] && echo "b is set"
A.Danischewski,2016年

您也可以使用更简单[[ ${xs[b]+set} ]]
阿恩·L.

5

有一种方法可以测试关联数组的元素是否存在(未设置),这不同于空:

isNotSet() {
    if [[ ! ${!1} && ${!1-_} ]]
    then
        return 1
    fi
}

然后使用它:

declare -A assoc
KEY="key"
isNotSet assoc[${KEY}]
if [ $? -ne 0 ]
then
  echo "${KEY} is not set."
fi

只是一张纸条:申报-A不上的bash 3.2.39(Debian的莱尼)没有工作,但它工作在bash的4.1.5(Debian的挤)
帕维尔Polewicz

关联数组是在猛砸4.介绍
迭戈F.杜兰

1
注意if ! some_check then return 1= some_check。因此:isNotSet() { [[ ... ]] }。请在下面检查我的解决方案,只需简单检查即可。
tokland 2012年

3

您可以通过将数组的内容传递给grep来查看是否存在条目。

 printf "%s\n" "${mydata[@]}" | grep "^${val}$"

您还可以使用grep -n获取条目的索引,该索引返回匹配项的行号(请记住减去1以获取从零开始的索引)。除了非常大的数组之外,这将相当快。

# given the following data
mydata=(a b c "hello world")

for val in a c hello "hello world"
do
           # get line # of 1st matching entry
    ix=$( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 )

    if [[ -z $ix ]]
    then
        echo $val missing
    else
         # subtract 1.  Bash arrays are zero-based, but grep -n returns 1 for 1st line, not 0 
        echo $val found at $(( ix-1 ))
    fi
done

a found at 0
c found at 2
hello missing
hello world found at 3

说明:

  • $( ... ) 与使用反引号将命令的输出捕获到变量中相同
  • printf 输出mydata每行一个元素
  • (所有必需的引号,以及与 此@相反的是,*.避免将“ hello world”分成两行)
  • grep搜索确切的字符串: ^$匹配行的开头和结尾
  • grep -n 以4:hello world的形式返回行号
  • grep -m 1 仅找到第一个比赛
  • cut 仅提取行号
  • 从返回的行号中减去1。

您当然可以将减法折叠到命令中。但是然后测试-1是否丢失:

ix=$(( $( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 ) - 1 ))

if [[ $ix == -1 ]]; then echo missing; else ... fi
  • $(( ... )) 做整数算术

1

我认为除非循环中的数据非常有限,否则您无法在没有循环的情况下正确地进行操作。

这是一个简单的变体,可以正确地说它"Super User"存在于数组中。但是它也可以说"uper Use"在数组中。

MyArray=('Super User' 'Stack Overflow' 'Server Fault' 'Jeff' );
FINDME="Super User"

FOUND=`echo ${MyArray[*]} | grep "$FINDME"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

#
# If you where to add anchors < and > to the data it could work
# This would find "Super User" but not "uper Use"
#

MyArray2=('<Super User>' '<Stack Overflow>' '<Server Fault>' '<Jeff>' );

FOUND=`echo ${MyArray2[*]} | grep "<$FINDME>"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

问题在于,除了循环遍历数组之外,没有简单的方法可以添加锚(我能想到的)。除非您可以在将它们放入数组之前添加它们...


但是,当常量为字母数字时,这是一个很好的解决方案grep "\b$FINDME\b"。可能可以与没有空格的非字母数字常量一起使用"(^| )$FINDME(\$| )"(或类似的东西……我从来无法了解正则表达式grep使用的是什么味道。)
Tgr 2010年

1
#!/bin/bash
function in_array {
  ARRAY=$2
  for e in ${ARRAY[*]}
  do
    if [[ "$e" == "$1" ]]
    then
      return 0
    fi
  done
  return 1
}

my_array=(Drupal Wordpress Joomla)
if in_array "Drupal" "${my_array[*]}"
  then
    echo "Found"
  else
    echo "Not found"
fi

1
您能否详细说明为什么建议这种方法?OP询问是否有一种方法可以不循环遍历数组,这就是您正在执行的操作in_array。干杯
bertieb '18

好吧,至少该循环被封装在一个函数中,这对于许多情况(使用小型数据集)来说可能已经足够好了,并且不需要bash 4+。可能${ARRAY[@]}应该使用。
Tobias
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.