Перейти к содержимому

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

14

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

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

83 270 просмотров
Tgr спросил 15 лет назад
T 1 563

6 ответов

21
Принятый ответ

В 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 · 15 лет назад · 0

$ прост, но имеет проблему: он не будет работать, если вы будете использовать set -u в своих скриптах (что рекомендуется), так как вы получите" несвязанную переменную ".

tokland · 14 лет назад · 1

@tokland: кто это рекомендует? Я конечно не

Dennis Williamson · 14 лет назад · 0

@DennisWilliamson: Хорошо, * некоторые люди * рекомендуют это, но я думаю, что было бы неплохо иметь решение, которое работает независимо от значения этих флагов.

tokland · 14 лет назад · 0
Dennis Williamson ответил 15 лет назад
D 75 594
8

Это старый вопрос, но я думаю, что самое простое решение еще не появилось 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 · 10 лет назад · 2
tokland ответил 14 лет назад
T 705
5

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

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 · 14 лет назад · 0

Ассоциативные массивы были введены в Bash 4.

Diego F. Durán · 14 лет назад · 0

обратите внимание, что если! some_check затем возвращает 1 = some_check. Итак: isNotSet () {[[...]]}. Проверьте мое решение ниже, вы можете сделать это в простой проверке.

tokland · 14 лет назад · 1
Diego F. Durán ответил 14 лет назад
D 171
3

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
kane ответил 14 лет назад
K 31
1

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

Вот один простой вариант, это правильно сказал бы, что "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 · 15 лет назад · 0
Nifle ответил 15 лет назад
N 27 786
1
#!/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 · 8 лет назад · 1

Ну, по крайней мере, этот цикл заключен в функцию, что может быть достаточно для многих случаев (с небольшими наборами данных) и не требует bash 4+. Вероятно, следует использовать $ .

Tobias · 7 лет назад · 0
Cong Nguyen ответил 8 лет назад
C 11