Как проверить, есть ли строка в массиве без итерации по элементам?
Есть ли способ проверить, существует ли строка в массиве строк - без итерации через массив?
Например, учитывая 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"
Просто обработайте расширенный массив как строку и проверьте подстроку, но чтобы изолировать первый и последний элемент, чтобы убедиться, что они не совпадают как часть подстроки с меньшим количеством включенных, они должны быть проверены отдельно.