Есть хороший способ проверить, есть ли в массиве элемент в bash (лучше, чем циклически проходить)?
В качестве альтернативы, есть ли другой способ проверить, равно ли число или строка какой-либо из набора предопределенных констант?
В 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)
Это старый вопрос, но я думаю, что самое простое решение еще не появилось: 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
Чтобы увидеть, как это работает, проверьте это.
Есть способ проверить, существует ли элемент ассоциативного массива (не установлен), это отличается от пустого:
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
Вы можете увидеть, присутствует ли запись, отправив содержимое массива в 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" на 2 строки) grep
ищет точную строку: ^
и $
соответствуют началу и концу строки grep -n
возвращает строку # в форме 4: привет мир grep -m 1
находит только первое совпадениеcut
выдержки только номер строки Конечно, вы можете сложить вычитание в команду. Но тогда проверьте -1 на отсутствие:
ix=$(( $( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 ) - 1 ))
if [[ $ix == -1 ]]; then echo missing; else ... fi
$(( ... ))
делает целочисленную арифметикуЯ не думаю, что вы можете сделать это правильно без циклов, если у вас нет очень ограниченных данных в массиве.
Вот один простой вариант, это правильно скажет, что в массиве существует "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
Проблема в том, что нет простого способа добавить якоря (о которых я могу думать), кроме циклического перемещения по массиву. Если вы не можете добавить их, прежде чем поместить их в массив ...
#!/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