Проверить, находится ли элемент в массиве в bash

83118
Tgr

Есть хороший способ проверить, есть ли в массиве элемент в bash (лучше, чем циклически проходить)?

В качестве альтернативы, есть ли другой способ проверить, равно ли число или строка какой-либо из набора предопределенных констант?

14

6 ответов на вопрос

21
Dennis Williamson

В 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 [[ $ ]]; then echo "Exists"; fi # Exists if [[ $ ]]; then echo "Exists"; fi # doesn't 

Для первоначальной настройки массива вы также можете выполнить прямые назначения:

array[foo]=1 array[bar]=1 # etc. 

или так:

array=([foo]=1 [bar]=1 [baz]=1) 
На самом деле, тест [[]] не работает, если значение пустое. Например, "array ['test'] = ''". В этом случае ключ 'test' существует, и вы можете увидеть его в списке с $ {! Array [@]}, но "[[$ ]]; echo $?" Отголоски 1, а не 0. haridsv 13 лет назад 0
`$ ` прост, но имеет проблему: он не будет работать, если вы будете использовать `set -u` в своих скриптах (что рекомендуется), так как вы получите" несвязанную переменную ". tokland 12 лет назад 1
@tokland: кто это рекомендует? Я конечно не Dennis Williamson 12 лет назад 0
@DennisWilliamson: Хорошо, * некоторые люди * рекомендуют это, но я думаю, что было бы неплохо иметь решение, которое работает независимо от значения этих флагов. tokland 12 лет назад 0
Некоторые сообщения о `set -u`: http://www.davidpashley.com/articles/writing-robust-shell-scripts.html, http://blog.hashbang0.com/2010/05/18/robust-bash -scripts-part-one /, http://www.openews.net/2012/bash-script-to-write-robust/. tokland 12 лет назад 0
8
tokland

Это старый вопрос, но я думаю, что самое простое решение еще не появилось test $. Пример:

declare -A xs=([a]=1 [b]="") test $ && echo "a is set" test $ && echo "b is set" test $ && echo "c is set" 

Выходы:

a is set b is set 

Чтобы увидеть, как это работает, проверьте это .

Информационное руководство рекомендует вам использовать `env`, чтобы избежать двусмысленности в псевдонимах, прогах и других функциях, которые могли принять название« тест ». Как указано выше, `env test $ && echo" a is set "`. Вы также можете получить эту функциональность, используя двойные скобки, тот же трюк с проверкой на нуль: `[[! -z "$ "]] && echo "b установлено" ` A.Danischewski 8 лет назад 2
5
Diego F. Durán

Есть способ проверить, существует ли элемент ассоциативного массива (не установлен), это отличается от пустого:

isNotSet() { if [[ ! ${!1} && ${!1-_} ]] then return 1 fi } 

Тогда используйте это:

declare -A assoc KEY="key" isNotSet assoc[$] if [ $? -ne 0 ] then echo "$ is not set." fi 
Примечание: -A не работает на bash 3.2.39 (debian lenny), но работает на bash 4.1.5 (debian squeeze) Paweł Polewicz 12 лет назад 0
Ассоциативные массивы были введены в Bash 4. Diego F. Durán 12 лет назад 0
обратите внимание, что `если! some_check затем возвращает 1` = `some_check`. Итак: `isNotSet () {[[...]]}`. Проверьте мое решение ниже, вы можете сделать это в простой проверке. tokland 12 лет назад 1
3
kane

You can see if an entry is present by piping the contents of the array to grep.

 printf "%s\n" "$" | grep "^$$" 

You can also get the index of an entry with grep -n, which returns the line number of a match (remember to subtract 1 to get zero-based index) This will be reasonably quick except for very large arrays.

# 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" "$" | grep -n -m 1 "^$$" | 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 

explanation:

  • $( ... ) is the same as using backticks to capture output of a command into a variable
  • printf outputs mydata one element per line
  • (all quotes necessary, along with @ instead of *. this avoids splitting "hello world" into 2 lines)
  • grep searches for exact string: ^ and $ match beginning and end of line
  • grep -n returns line #, in form of 4:hello world
  • grep -m 1 finds first match only
  • cut extracts just the line number
  • subtract 1 from returned line number.

You can of course fold the subtraction into the command. But then test for -1 for missing:

ix=$(( $( printf "%s\n" "$" | grep -n -m 1 "^$$" | cut -d ":" -f1 ) - 1 )) if [[ $ix == -1 ]]; then echo missing; else ... fi 
  • $(( ... )) does integer arithmetic
1
Nifle

Я не думаю, что вы можете сделать это правильно без циклов, если у вас нет очень ограниченных данных в массиве.

Вот один простой вариант, это правильно сказал бы, что "Super User"существует в массиве. Но это также сказал бы, что "uper Use"находится в массиве.

MyArray=('Super User' 'Stack Overflow' 'Server Fault' 'Jeff' ); FINDME="Super User"  FOUND=`echo $ | grep "$FINDME"`  if [ "$" != "" ]; 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 $ | grep "<$FINDME>"`  if [ "$" != "" ]; then echo Array contains: $FINDME else echo $FINDME not found fi 

Проблема в том, что нет простого способа добавить якоря (о которых я могу думать), кроме циклического перемещения по массиву. Если вы не можете добавить их, прежде чем поместить их в массив ...

Это хорошее решение, когда константы буквенно-цифровые, хотя (с `grep" \ b $ FINDME \ b "`). Вероятно, может работать с не алфавитно-цифровыми константами, которые не имеют пробелов, с `" (^ |) $ FINDME (\ $ |) "` (или что-то в этом роде ... Мне никогда не удавалось узнать, какой вариант использования regexp grep использует .) Tgr 13 лет назад 0
1
Cong Nguyen
#!/bin/bash function in_array { ARRAY=$2 for e in $ do if [[ "$e" == "$1" ]] then return 0 fi done return 1 }  my_array=(Drupal Wordpress Joomla) if in_array "Drupal" "$" then echo "Found" else echo "Not found" fi 
Можете ли вы уточнить, почему вы предлагаете этот подход? OP спросил, есть ли способ сделать это * без циклического перебора массива *, что вы и делаете в in_array. ура bertieb 6 лет назад 1
Ну, по крайней мере, этот цикл заключен в функцию, что может быть достаточно для многих случаев (с небольшими наборами данных) и не требует bash 4+. Вероятно, следует использовать `$ `. Tobias 6 лет назад 0