Как проверить, есть ли строка в массиве без итерации по элементам?

Есть ли способ проверить, существует ли строка в массиве строк - без итерации через массив?

Например, учитывая script ниже, как я могу правильно реализовать его, чтобы проверить, существует ли значение, хранящееся в переменной $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

Ответ 1

С 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

Ответ 2

Там всегда будет технически итерация, но она может быть отнесена к оболочке, лежащей в основе кода массива. Расширения оболочки предлагают абстракцию, которая скрывает детали реализации, и избегает необходимости явного цикла внутри оболочки script.

Обработка границ слова для этого варианта использования проще с помощью 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

Ответ 3

Вы можете использовать ассоциативный массив, так как вы используете 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", если элемент массива не установлен (но не имеет значения null). Делая это и проверяя, отличается ли результат от исходного значения, мы можем определить, существует ли ключ независимо от его значения.

Ответ 4

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

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

Ответ 5

Чтение сообщения. Я понимаю, что вы просто не хотите знать, существует ли строка в массиве (как будет указано в заголовке), но чтобы узнать, действительно ли эта строка соответствует элементу этого массива. Если это так, читайте дальше.

Я нашел способ, который, кажется, работает нормально.

Полезно, если вы используете стек с 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 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.

  • Поэтому мы должны установить IFS на то, что мы хотим использовать в качестве границы (двоеточие в моем случае):

    IFS=:
    
  • Затем мы обертываем элемент, который мы ищем тем же символом:

    test=:henry:
    
  • И, наконец, мы ищем его в массиве. Обратите внимание на правила, которые я выполнил для выполнения теста (все они обязательны): массив имеет двойную кавычку, * используется (@не подходит) И он завернут в пограничный символ, который я установил IFS раньше:

    [[ ":${array[*]}:" =~ $test ]] && echo found || echo "not found :("
    not found :(
    
  • Если мы ищем существующий элемент:

    test=:perseus:
    [[ ":${array[*]}:" =~ $test ]] && echo "found! :)" || echo "not found :("
    found! :)
    
  • Для другого теста мы можем изменить последний элемент "perseus" для "perseus smith" (элемент с пробелами), просто чтобы проверить, соответствует ли оно (что не должно быть):

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

    Отлично! Это ожидаемый результат, так как "perseus" сам по себе больше не является элементом.

  • Важно!: Не забудьте отменить IFS, чтобы вернуть его по умолчанию (unset) после завершения тестов:

    unset IFS
    

Итак, пока этот метод работает, вам просто нужно быть осторожным и выбрать символ для IFS, чтобы убедиться, что ваши элементы не будут содержать.

Надеюсь, это поможет кому угодно!

С уважением, Фред

Ответ 6

Вместо итерации по элементам массива можно использовать расширение параметра для удаления указанной строки в качестве элемента массива (для получения дополнительной информации и примеров см. Мессинг с массивы в bash и Изменить каждый элемент массива Bash без цикла).

(
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
)

Ответ 7

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

Ответ 8

#!/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"

Просто обработайте расширенный массив как строку и проверьте подстроку, но чтобы изолировать первый и последний элемент, чтобы убедиться, что они не совпадают как часть подстроки с меньшим количеством включенных, они должны быть проверены отдельно.