如何在不迭代元素的情况下检查字符串是否在数组中?

时间:2012-07-09 14:10:06

标签: bash

有没有办法检查字符串数组中是否存在字符串 - 而不是遍历数组?

例如,根据下面的脚本,如何正确实现它以测试存储在变量$ test中的值是否存在于$ array中?

array=('hello' 'world' 'my' 'name' 'is' 'perseus')

#pseudo code
$test='henry'
if [$array[$test]]
   then
      do something
   else
      something else
fi

注意

我正在使用bash 4.1.5

9 个答案:

答案 0 :(得分:12)

使用bash 4,你可以做的最接近的事情就是使用关联数组。

declare -A map
for name in hello world my name is perseus; do
  map["$name"]=1
done

... 完全完全相同:

declare -A map=( [hello]=1 [my]=1 [name]=1 [is]=1 [perseus]=1 )

......接着是:

tgt=henry
if [[ ${map["$tgt"]} ]] ; then
  : found
fi

答案 1 :(得分:5)

总是技术上是迭代,但它可以降级到shell的底层数组代码。 Shell expansions提供隐藏实现细节的抽象,并避免在shell脚本中显式循环的必要性。

使用 fgrep 可以更轻松地处理此用例的字边界,它具有处理全字固定字符串的内置工具。正则表达式匹配更难以正确,但下面的示例适用于提供的语料库。

外部Grep过程

array=('hello' 'world' 'my' 'name' 'is' 'perseus')
word="world"
if echo "${array[@]}" | fgrep --word-regexp "$word"; then
    : # do something
fi

Bash正则表达式测试

array=('hello' 'world' 'my' 'name' 'is' 'perseus')
word="world"
if [[ "${array[*]}" =~ (^|[^[:alpha:]])$word([^[:alpha:]]|$) ]]; then
    : # do something
fi

答案 2 :(得分:4)

您可以使用关联数组,因为您正在使用Bash 4。

declare -A array=([hello]= [world]= [my]= [name]= [is]= [perseus]=)

test='henry'
if [[ ${array[$test]-X} == ${array[$test]} ]]
then
    do something
else
    something else
fi

如果未设置数组元素,则参数扩展将替换为“X”(但如果数组元素为空则不会替换为“X”)。通过这样做并检查结果是否与原始值不同,我们可以判断密钥是否存在,无论其值如何。

答案 3 :(得分:2)

array=('hello' 'world' 'my' 'name' 'is' 'perseus')
regex="^($(IFS=\|; echo "${array[*]}"))$"

test='henry'
[[ $test =~ $regex ]] && echo "found" || echo "not found"

答案 4 :(得分:1)

阅读你的帖子我认为你不仅想知道数组中是否存在字符串(如标题所示),而是要知道该字符串是否实际上对应于该数组的元素。如果是这种情况,请继续阅读。

我找到了一种似乎工作正常的方法。

如果您像我一样使用bash 3.2进行堆栈(但也在bash 4.2中进行了测试和工作),这很有用:

array=('hello' 'world' 'my' 'name' 'is' 'perseus')
IFS=:     # We set IFS to a character we are confident our 
          # elements won't contain (colon in this case)

test=:henry:        # We wrap the pattern in the same character

# Then we test it:
# Note the array in the test is double quoted, * is used (@ is not good here) AND 
# it's wrapped in the boundary character I set IFS to earlier:
[[ ":${array[*]}:" =~ $test ]] && echo "found! :)" || echo "not found :("
not found :(               # Great! this is the expected result

test=:perseus:      # We do the same for an element that exists
[[ ":${array[*]}:" =~ $test ]] && echo "found! :)" || echo "not found :("
found! :)               # Great! this is the expected result

array[5]="perseus smith"    # For another test we change the element to an 
                            # element with spaces, containing the original pattern.

test=:perseus:
[[ ":${array[*]}:" =~ $test ]] && echo "found!" || echo "not found :("
not found :(               # Great! this is the expected result

unset IFS        # Remember to unset IFS to revert it to its default value  

让我解释一下:

此解决方法基于"${array[*]}"(注意双引号和星号)扩展到由IFS的第一个字符分隔的数组元素列表的原则。

  1. 因此我们必须将IFS设置为我们想要用作边界的任何东西(在我的例子中是冒号):

    IFS=:
    
  2. 然后我们将要查找的元素包装在同一个字符中:

    test=:henry:
    
  3. 最后我们在数组中寻找它。注意我执行测试时遵循的规则(它们都是必需的):数组是双引号,*是用的(@不好)它包含在我将IFS设置为更早的边界字符中:

    [[ ":${array[*]}:" =~ $test ]] && echo found || echo "not found :("
    not found :(
    
  4. 如果我们寻找存在的元素:

    test=:perseus:
    [[ ":${array[*]}:" =~ $test ]] && echo "found! :)" || echo "not found :("
    found! :)
    
  5. 对于另一个测试,我们可以更改'perseus smith'的最后一个元素'perseus'(带空格的元素),只是为了检查它是否匹配(不应该是):

    array[5]="perseus smith"
    test=:perseus:
    [[ ":${array[*]}:" =~ $test ]] && echo "found!" || echo "not found :("
    not found :(
    

    太棒了!这是预期的结果,因为“perseus”本身不再是一个元素。

  6. 重要!:完成测试后,请记得取消设置IFS以将其恢复为默认值(未设置):

    unset IFS
    
  7. 所以到目前为止这个方法似乎有效,你只需要小心并为IFS选择一个你确定你的元素不会包含的字符。

    希望它对任何人都有帮助!

    此致 佛瑞德

答案 5 :(得分:0)

不是迭代数组元素,而是可以使用参数扩展将指定的字符串作为数组项删除(有关详细信息和示例,请参阅Messing with arrays in bashModify every element of a Bash array without looping)。

(
set -f
export IFS=""

test='henry'
test='perseus'

array1=('hello' 'world' 'my' 'name' 'is' 'perseus')
#array1=('hello' 'world' 'my' 'name' 'is' 'perseusXXX' 'XXXperseus')

# removes empty string as array item due to IFS=""
array2=( ${array1[@]/#${test}/} )

n1=${#array1[@]}
n2=${#array2[@]}

echo "number of array1 items: ${n1}"
echo "number of array2 items: ${n2}"
echo "indices of array1: ${!array1[*]}"
echo "indices of array2: ${!array2[*]}"

echo 'array2:'
for ((i=0; i < ${#array2[@]}; i++)); do 
   echo "${i}: '${array2[${i}]}'"
done

if [[ $n1 -ne $n2 ]]; then
   echo "${test} is in array at least once! "
else
   echo "${test} is NOT in array! "
fi
)

答案 6 :(得分:0)

q=( 1 2 3 )
[ "${q[*]/1/}" = "${q[*]}" ] && echo not in array || echo in array 
#in array
[ "${q[*]/7/}" = "${q[*]}" ] && echo not in array || echo in array 
#not in array

答案 7 :(得分:0)

#!/bin/bash

test="name"

array=('hello' 'world' 'my' 'yourname' 'name' 'is' 'perseus')
nelem=${#array[@]}
[[ "${array[0]} " =~ "$test " ]] || 
[[ "${array[@]:1:$((nelem-1))}" =~ " $test " ]] || 
[[ " ${array[$((nelem-1))]}" =~ " $test" ]] && 
echo "found $test" || echo "$test not found"

只需将扩展数组视为字符串并检查子字符串,但要隔离第一个和最后一个元素以确保它们不匹配作为较少包含的子字符串的一部分,必须单独测试它们。

答案 8 :(得分:0)

在大多数情况下,以下方法将起作用。当然,它具有限制和局限性,但易于阅读和理解。

if [ "$(echo " ${array[@]} " | grep " $test ")" == "" ]; then
    echo notFound
else
    echo found
fi