Der einfachste Weg, um in einem Array nach einem Index oder einem Schlüssel zu suchen?

85

Verwenden von:

set -o nounset
  1. Mit einem indizierten Array wie:

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

    Was ist der kürzeste Weg, um zu überprüfen, ob Element 1 gesetzt ist?
    Ich benutze manchmal Folgendes:

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

    Ich würde gerne wissen, ob es eine bevorzugte gibt.

  2. Wie gehe ich mit nicht aufeinanderfolgenden Indizes um?

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

    Wie kann man schnell überprüfen, ob das 51zum Beispiel schon eingestellt ist?

  3. Wie gehe ich mit assoziativen Arrays um?

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

    Wie kann man schnell überprüfen, ob dies key2beispielsweise bereits verwendet wird?

Luca Borrione
quelle

Antworten:

129

Überprüfen, ob das Element festgelegt ist (gilt sowohl für indizierte als auch für assoziative Arrays)

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

Im Grunde , was ${array[key]+abc}tut , ist

  • Wenn array[key]gesetzt, kehren Sie zurückabc
  • Wenn array[key]nicht festgelegt, wird nichts zurückgegeben


Verweise:

  1. Siehe Parametererweiterung im Bash-Handbuch und den kleinen Hinweis

    Wenn der Doppelpunkt weggelassen wird, prüft der Operator nur, ob [der Parameter ] vorhanden ist.

  2. Diese Antwort wurde tatsächlich aus den Antworten für diese SO-Frage übernommen: Wie kann man feststellen, ob eine Zeichenfolge nicht in einem Bash-Shell-Skript definiert ist ?


Eine Wrapper-Funktion:

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

Beispielsweise

if ! exists key in array; then echo "No such array element"; fi 
doubleDown
quelle
Ich habe folgendermaßen gelöst: if test "$ {myArray ['key_or_index'] + isset}"; dann Echo "Ja"; sonst echo "nein"; fi; Es scheint mir der einfachste Weg zu sein und gilt für indizierte und assoziative Arrays. Vielen Dank
Luca Borrione
1
@doubleDown Wie verwendet man [$ {array [key] + abc}] in einer if-Klausel, um nur dann etwas zu tun, wenn [$ {array [key] + abc}] nicht existiert?
Olala
1
Funktioniert auch nicht, wenn Sie versehentlich ein aufgezähltes Array als assoziatives abfragen.
Tomáš Zato - Wiedereinsetzung Monica
1
@duanev: Ohne +abc, [ ${array[key]} ]wird falsch beurteilen , wenn das Element in der Tat gesetzt ist , aber auf einen leeren Wert, so dass es die Prüfung tatsächlich den Wert Nicht-Leere anstatt die Schlüssel Existenz.
Musiphil
@duanev Ohne +abcauch fehlgeschlagen, wenn array[key]nicht gesetzt und set -uwirksam ist.
Ding-Yi Chen
35

Von Man Bash , bedingte Ausdrücke:

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

Beispiel:

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

Dies zeigt, dass sowohl foo [bar] als auch foo [baz] gesetzt sind (obwohl letzterer auf einen leeren Wert gesetzt ist) und foo [quux] nicht.

Vineet
quelle
1
Ich habe es auf einen Blick verpasst; Beachten Sie, dass die typische Array-Erweiterungssyntax nicht verwendet wird.
Nathan Chappell
Mit set -u, warum [[ -v "${foo[bar]}" ]]ein ungebundenen Variable Fehler erzeugen , wenn barnicht im Wörterbuch vorhanden? Funktioniert gut ohne die ${}; Ich bin es einfach gewohnt, es standardmäßig für alles zu verwenden.
bgfvdu3w
"${foo[bar]}"wertet zuerst die Array-Variable aus, daher [[ -vtestet der Befehl nach einer Variablen mit dem Namen dieses Werts
andysh
8

Neue Antwort

Ab Version 4.2 von (und neuer) gibt es eine neue -vOption für den eingebauten testBefehl.

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

Dies funktioniert mit assoziativen Arrays auf die gleiche Weise:

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

Mit einem kleinen Unterschied:
In regulären Arrays ist die Variable zwischen Klammern ( [i]) eine Ganzzahl, daher ist das Dollarsymbol ( $) nicht erforderlich, aber für ein assoziatives Array ist als Schlüssel ein Wort $erforderlich ( [$i])!

Alte Antwort für vor V4.2

Leider gibt Bash keine Möglichkeit, einen Unterschied zwischen leeren und undefinierten Variablen zu machen.

Es gibt jedoch einige Möglichkeiten:

$ 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

(keine Antwort geben)

Und für assoziative Arrays können Sie dasselbe verwenden:

$ 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

Sie könnten die Arbeit ohne externe Tools erledigen (kein printf | grep als reine Bash ), und warum nicht, checkIfExist () als neue Bash-Funktion erstellen :

$ 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

oder erstellen Sie sogar eine neue getIfExist- Bash-Funktion, die den gewünschten Wert zurückgibt und mit falschem Ergebniscode beendet wird, wenn der gewünschte Wert nicht vorhanden ist:

$ 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
F. Hauri
quelle
Ok für Downvotes: Diese Antwort wurde vor V4.2 von bash gepostet ! Antwort bearbeitet!
F. Hauri
Funktioniert nicht weiter bash 4.2.46. Funktioniert weiter bash 4.4.12.
Irfy
@Irfy Was funktioniert nicht? -vOption testoder getIfExistFunktion?
F. Hauri
Das -vfunktioniert nicht auf Arrays unter meinem CentOS 7.7.1908 mit Bash 4.2.46. Der Code aus Ihrem ersten Codeblock wird not existin allen Fällen unter dieser Bash gedruckt . (Auch versucht [$i]statt [i], kein Unterschied)
Irfy
5

getestet in Bash 4.3.39 (1) -Release

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
gdoubleod
quelle
Dies schlägt fehl, wenn der Wert des Schlüssels eine leere Zeichenfolge ist. +Um dieses Problem zu umgehen, können Sie die Parametererweiterung verwenden, um einen leeren Wert durch einen Platzhalter wie einen Unterstrich zu ersetzen. Zum Beispiel declare -A a[x]=;[[ ${a[x]} ]];echo $?druckt 1, aber declare -A a[x]=;[[ ${a[x]+_} ]];echo $?druckt 0.
Nisetama
3

Was ist mit einem -zTest und dem :-Bediener?

Zum Beispiel dieses Skript:

#!/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

Drucke:

ABC is set
DEF is set
GuyPaddock
quelle
Tolle kompakte Lösung, die wie erwartet auf eine leere Zeichenfolge reagiert
Ryan Dugan
1

Dies ist der einfachste Weg, den ich für Skripte gefunden habe.

<search>ist die Zeichenfolge, die Sie suchen möchten, ASSOC_ARRAYder Name der Variablen, die Ihr assoziatives Array enthält.

Abhängig davon, was Sie erreichen möchten:

Schlüssel existiert :

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

Schlüssel existiert nicht :

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

Wert existiert :

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

Wert existiert nicht :

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

Ich habe eine Funktion geschrieben, um zu überprüfen, ob ein Schlüssel in einem Array in Bash vorhanden ist:

# 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
}

Beispiel

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

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

Getestet mit GNU Bash, Version 4.1.5 (1) -Veröffentlichung (i486-pc-linux-gnu)

Lucas Stad
quelle