Найпростіший спосіб перевірити наявність індексу чи ключа в масиві?


89

Використання:

set -o nounset
  1. Наявність індексованого масиву, як:

    myArray=( "red" "black" "blue" )
    

    Який найкоротший спосіб перевірити, чи встановлений елемент 1?
    Іноді я використовую наступне:

    test "${#myArray[@]}" -gt "1" && echo "1 exists" || echo "1 doesn't exist"
    

    Я хотів би знати, чи є один з них кращий.

  2. Як боротися з непослідовними індексами?

    myArray=()
    myArray[12]="red"
    myArray[51]="black"
    myArray[129]="blue"
    

    Як швидко перевірити, що 51вже встановлено, наприклад?

  3. Як мати справу з асоціативними масивами?

    declare -A myArray
    myArray["key1"]="red"
    myArray["key2"]="black"
    myArray["key3"]="blue"
    

    Як швидко перевірити, що key2вже використовується, наприклад?

Відповіді:


130

Щоб перевірити, чи встановлений елемент (застосовується як до індексованого, так і до асоціативного масиву)

[ ${array[key]+abc} ] && echo "exists"

В основному те, що ${array[key]+abc}робить, це

  • якщо array[key]встановлено, повернітьabc
  • якщо array[key]не встановлено, нічого не повертати


Список літератури:

  1. Див. Розширення параметрів у посібнику Bash та невелику примітку

    якщо двокрапка пропущена, оператор перевіряє лише наявність [ параметра ]

  2. Ця відповідь фактично адаптована з відповідей на це запитання SO: Як визначити, чи рядок не визначений у сценарії оболонки bash ?


Функція обгортки:

exists(){
  if [ "$2" != in ]; then
    echo "Incorrect usage."
    echo "Correct usage: exists {key} in {array}"
    return
  fi   
  eval '[ ${'$3'[$1]+muahaha} ]'  
}

Наприклад

if ! exists key in array; then echo "No such array element"; fi 

Я вирішив таким чином: якщо тест "$ {myArray ['key_or_index'] + isset}"; потім луна «так»; інакше луна "ні"; fi; Мені здається, це найпростіший спосіб, і це стосується індексованих та асоціативних масивів. Дякую
Luca Borrione

1
@doubleDown Як ви використовуєте [$ {array [key] + abc}] у реченні if, щоб щось робити, лише якщо [$ {array [key] + abc}]] не існує?
олала

1
Також не працює, коли ви випадково запитуєте перерахований масив як асоціативний.
Томаш Зато - відновити Моніку

1
@duanev: Без +abc, [ ${array[key]} ]буде оцінювати як false, якщо елемент дійсно встановлений, але має порожнє значення, тому він фактично перевіряє непустоту значення, а не існування ключа.
musiphil

@duanev Без +abcтакож не вдалося, коли array[key]не встановлено і set -uдіє.
Ding-Yi Chen

35

Від man bash , умовні вирази:

-v varname
              True if the shell variable varname is set (has been assigned a value).

приклад:

declare -A foo
foo[bar]="this is bar"
foo[baz]=""
if [[ -v "foo[bar]" ]] ; then
  echo "foo[bar] is set"
fi
if [[ -v "foo[baz]" ]] ; then
  echo "foo[baz] is set"
fi
if [[ -v "foo[quux]" ]] ; then
  echo "foo[quux] is set"
fi

Це покаже, що і foo [bar], і foo [baz] встановлені (навіть якщо для останнього встановлено порожнє значення), а foo [quux] - ні.


1
Я пропустив це швидким поглядом; зверніть увагу, що типовий синтаксис розширення масиву не використовується.
Nathan Chappell,

З set -u, чому [[ -v "${foo[bar]}" ]]виробляти незв'язану помилку змінної , якщо barне існує в словнику? Прекрасно працює без ${}; Я просто звик використовувати його для всього за замовчуванням.
bgfvdu3w

"${foo[bar]}"спочатку обчислює змінну масиву, тому [[ -vкоманда перевіряє змінну з іменем цього значення
andysh

10

Нова відповідь

З версії 4.2 (і новіше), є новий -vваріант вбудованої testкоманди.

array=([12]="red" [51]="black" [129]="blue")

for i in 10 12 30 {50..52} {128..131};do
    if [ -v array[i] ];then
        echo "Variable 'array[$i]' is defined"
    else
        echo "Variable 'array[$i]' not exist"
    fi
done
Variable 'array[10]' not exist
Variable 'array[12]' is defined
Variable 'array[30]' not exist
Variable 'array[50]' not exist
Variable 'array[51]' is defined
Variable 'array[52]' not exist
Variable 'array[128]' not exist
Variable 'array[129]' is defined
Variable 'array[130]' not exist
Variable 'array[131]' not exist

Це працює з асоціативними масивами так само:

declare -A aArray=([foo]="bar" [bar]="baz" [baz]=$'Hello world\041')

for i in alpha bar baz dummy foo test;do
    if [ -v aArray[$i] ];then
        echo "Variable 'aArray[$i]' is defined"
    else
        echo "Variable 'aArray[$i]' not exist"
    fi
done
Variable 'aArray[alpha]' not exist
Variable 'aArray[bar]' is defined
Variable 'aArray[baz]' is defined
Variable 'aArray[dummy]' not exist
Variable 'aArray[foo]' is defined
Variable 'aArray[test]' not exist

З невеликою різницею:
У звичайних масивах змінною між дужками ( [i]) є ціле число, тому символ долара ( $) не потрібен, але для асоціативного масиву, оскільки ключовим є слово, $потрібно ( [$i])!

Стара відповідь для до V4.2

На жаль, bash не дає можливості зробити різницю між порожньою та невизначеною змінною.

Але є кілька способів:

$ array=()
$ array[12]="red"
$ array[51]="black"
$ array[129]="blue"

$ echo ${array[@]}
red black blue

$ echo ${!array[@]}
12 51 129

$ echo "${#array[@]}"
3

$ printf "%s\n" ${!array[@]}|grep -q ^51$ && echo 51 exist
51 exist

$ printf "%s\n" ${!array[@]}|grep -q ^52$ && echo 52 exist

(не дайте відповіді)

А для асоціативного масиву ви можете використовувати те саме:

$ unset array
$ declare -A array
$ array["key1"]="red"
$ array["key2"]="black"
$ array["key3"]="blue"
$ echo ${array[@]}
blue black red

$ echo ${!array[@]}
key3 key2 key1

$ echo ${#array[@]}
3

$ set | grep ^array=
array=([key3]="blue" [key2]="black" [key1]="red" )

$ printf "%s\n" ${!array[@]}|grep -q ^key2$ && echo key2 exist || echo key2 not exist
key2 exist

$ printf "%s\n" ${!array[@]}|grep -q ^key5$ && echo key5 exist || echo key5 not exist
key5 not exist

Ви можете виконувати цю роботу без використання зовнішніх інструментів (немає printf | grep як чистого bash ), а чому б і ні, побудуйте checkIfExist () як нову функцію bash:

$ checkIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) return 0 ;;
        * ) return 1 ;;
      esac";
}

$ checkIfExist array key2 && echo exist || echo don\'t
exist

$ checkIfExist array key5 && echo exist || echo don\'t
don't

або навіть створити нову функцію basH getIfExist, яка повертає бажане значення і виходить із помилковим кодом результату, якщо бажане значення не існує:

$ getIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) echo \${$1[$2]};return 0 ;;
        * ) return 1 ;;
      esac";
}

$ getIfExist array key1
red
$ echo $?
0

$ # now with an empty defined value
$ array["key4"]=""
$ getIfExist array key4

$ echo $?
0
$ getIfExist array key5
$ echo $?
1

Гаразд за проти: Ця відповідь була опублікована до V4.2 bash ! Відповідь відредаговано!
Ф. Хаурі

Не працює bash 4.2.46. Працює далі bash 4.4.12.
Ірфі

@Irfy Що не працює? -vваріант testабо getIfExistфункція?
Ф. Хаурі

Це -vне працює з масивами на моєму CentOS 7.7.1908 з bash 4.2.46. Код з вашого першого блоку коду друкується not existу всіх випадках під цим bash. (Також намагався [$i]замість [i], без різниці)
Irfy

5

протестовано в bash 4.3.39 (1) -випуск

declare -A fmap
fmap['foo']="boo"

key='foo'
# should echo foo is set to 'boo'
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi
key='blah'
# should echo blah is unset in fmap
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi

Це не вдається, коли значення ключа є порожнім рядком. Як обхідне рішення ви можете використовувати +розширення параметра, щоб замінити порожнє значення деяким заповнювачем, таким як підкреслення. Наприклад declare -A a[x]=;[[ ${a[x]} ]];echo $?відбитки 1, але declare -A a[x]=;[[ ${a[x]+_} ]];echo $?відбитки 0.
nisetama

3

А як щодо -zтесту та :-оператора?

Наприклад, цей сценарій:

#!/usr/bin/env bash

set -e
set -u

declare -A sample

sample["ABC"]=2
sample["DEF"]=3

if [[ ! -z "${sample['ABC']:-}" ]]; then
  echo "ABC is set"
fi

if [[ ! -z "${sample['DEF']:-}" ]]; then
  echo "DEF is set"
fi

if [[ ! -z "${sample['GHI']:-}" ]]; then
  echo "GHI is set"
fi

Друк:

ABC is set
DEF is set

Чудове компактне рішення, яке відповідає очікуваному порожньому рядку
Райан Дуган

1

Це найпростіший спосіб, який я знайшов для сценаріїв.

<search>це рядок, який ви хочете знайти, ASSOC_ARRAYім'я змінної, що містить ваш асоціативний масив.

Залежно від того, чого ви хочете досягти:

ключ існує :

if grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key is present; fi

ключ не існує :

if ! grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key not present; fi

значення існує :

if grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value is present; fi

значення існує не :

if ! grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value not present; fi

1

Я написав функцію, щоб перевірити, чи існує ключ у масиві в Bash:

# Check if array key exists
# Usage: array_key_exists $array_name $key
# Returns: 0 = key exists, 1 = key does NOT exist
function array_key_exists() {
    local _array_name="$1"
    local _key="$2"
    local _cmd='echo ${!'$_array_name'[@]}'
    local _array_keys=($(eval $_cmd))
    local _key_exists=$(echo " ${_array_keys[@]} " | grep " $_key " &>/dev/null; echo $?)
    [[ "$_key_exists" = "0" ]] && return 0 || return 1
}

Приклад

declare -A my_array
my_array['foo']="bar"

if [[ "$(array_key_exists 'my_array' 'foo'; echo $?)" = "0" ]]; then
    echo "OK"
else
    echo "ERROR"
fi

Перевірено за допомогою GNU bash, версія 4.1.5 (1) -випуск (i486-pc-linux-gnu)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.