17

有没有一种方法可以检查字符串是否存在于字符串数组中 - 无需遍历数组?

例如,给定下面的脚本,我如何正确实现它来测试存储在变量 $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

4

10 回答 10

15

使用 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
于 2012-07-09T14:18:06.830 回答
9

从技术上讲,总会有迭代,但它可以归入 shell 的底层数组代码。Shell 扩展提供了隐藏实现细节的抽象,并避免了在 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
于 2012-07-09T14:24:13.143 回答
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”(但如果它为空则不会)。通过这样做并检查结果是否与原始值不同,我们可以判断键是否存在,而不管其值如何。

于 2012-07-09T14:18:28.383 回答
2
array=('hello' 'world' 'my' 'name' 'is' 'perseus')
regex="^($(IFS=\|; echo "${array[*]}"))$"

test='henry'
[[ $test =~ $regex ]] && echo "found" || echo "not found"
于 2014-03-26T10:29:15.680 回答
1

可以使用参数扩展将指定的字符串作为数组项删除,而不是迭代数组元素(有关更多信息和示例,请参阅Messing with arrays in bashModify each 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
)
于 2014-01-22T16:17:53.620 回答
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' 更改为 'perseus smith'(带空格的元素),只是为了检查它是否匹配(不应该):

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

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

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

    unset IFS
    

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

希望它可以帮助任何人!

问候,弗雷德

于 2015-04-29T10:42:08.227 回答
1

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

if [ "$(echo " ${array[@]} " | grep " $test ")" == "" ]; then
    echo notFound
else
    echo found
fi
于 2020-05-06T04:02:33.123 回答
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
于 2014-06-28T07:46:53.140 回答
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"

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

于 2014-06-28T08:02:32.690 回答
0

如果 !grep -q "$item" <<< "$itemlist" ; 然后 .....

应该可以正常工作。

于 2021-12-01T14:00:09.860 回答