Überprüfen Sie, ob ein Element in einem Bash-Array vorhanden ist [Duplikat]

75

Ich habe mich gefragt, ob es eine effiziente Möglichkeit gibt, zu überprüfen, ob ein Element in einem Array in Bash vorhanden ist. Ich suche etwas Ähnliches wie das, was ich in Python tun kann, wie:

arr = ['a','b','c','d']

if 'd' in arr:
    do your thing
else:
    do something

Ich habe Lösungen gesehen, die assoziatives Array für Bash für Bash 4+ verwenden, aber ich frage mich, ob es da draußen eine andere Lösung gibt.

Bitte haben Sie Verständnis dafür, dass ich weiß, dass die triviale Lösung darin besteht, im Array zu iterieren, aber das möchte ich nicht.

QuantumLicht
quelle
4
Verwechseln Sie "prägnant" nicht mit "effizient". Aber nein, es gibt keine präzise Möglichkeit bash, mit einem einfachen Array das zu tun, was Sie wollen.
Chepner
Werfen
Perleone

Antworten:

105

Du könntest es tun:

if [[ " ${arr[*]} " == *" d "* ]]; then
    echo "arr contains d"
fi

Dies führt beispielsweise zu falsch positiven Ergebnissen, wenn Sie nach "a b" suchen - dieser Teilstring befindet sich in der verknüpften Zeichenfolge, jedoch nicht als Array-Element. Dieses Dilemma tritt für jedes von Ihnen gewählte Trennzeichen auf.

Am sichersten ist es, das Array zu durchlaufen, bis Sie das Element finden:

array_contains () {
    local seeking=$1; shift
    local in=1
    for element; do
        if [[ $element == "$seeking" ]]; then
            in=0
            break
        fi
    done
    return $in
}

arr=(a b c "d e" f g)
array_contains "a b" "${arr[@]}" && echo yes || echo no    # no
array_contains "d e" "${arr[@]}" && echo yes || echo no    # yes

Hier ist eine "sauberere" Version, bei der Sie nur den Array-Namen und nicht alle Elemente übergeben

array_contains2 () { 
    local array="$1[@]"
    local seeking=$2
    local in=1
    for element in "${!array}"; do
        if [[ $element == "$seeking" ]]; then
            in=0
            break
        fi
    done
    return $in
}

array_contains2 arr "a b"  && echo yes || echo no    # no
array_contains2 arr "d e"  && echo yes || echo no    # yes

Bei assoziativen Arrays gibt es eine sehr übersichtliche Methode, um zu testen, ob das Array einen bestimmten Schlüssel enthält : den -vOperator

$ declare -A arr=( [foo]=bar [baz]=qux )
$ [[ -v arr[foo] ]] && echo yes || echo no
yes
$ [[ -v arr[bar] ]] && echo yes || echo no
no

Siehe 6.4 Bedingte Bash-Ausdrücke im Handbuch.

Glenn Jackman
quelle
Funktioniert dieser Code sowohl für numerische als auch für Zeichenfolgenwerte? Kann ich beispielsweise nach beiden suchen, wenn mein Array sowohl Zeichenfolgen als auch ein Array enthält?
QuantumLicht
Ja. Bash wird numerische Werte ohne Schwierigkeiten als Zeichenfolgen behandeln
Glenn Jackman
um das erste Element der Positionsparameter zu entfernen.
Glenn Jackman
1
In meiner Implementierung musste ich "$ {! Array}" in "$ {array [@]}" ändern, um zu funktionieren. Ich habe die Vorschläge "array_contains" und "array_contains2" als sicherer beurteilt, da viele, die grep verwenden, falsch positive Ergebnisse zurückgeben.
Eduardo Lucio
1
Erwägen Sie, zu zitieren, $seekingdamit es nicht als Muster betrachtet wird.
PesaDer
35

Abgesehen von offensichtlichen Einschränkungen könnten Sie dies tun, wenn Ihr Array tatsächlich dem oben genannten entspricht

if [[ ${arr[*]} =~ d ]]
then
  do your thing
else
  do something
fi
Steven Penny
quelle
4
+1 für das Unterrichten von etwas Neuem. dWird aber auch passen xdy.
Olaf Dietsche
3
@OlafDietsche Das könnte gelöst werden, indem man es schreibt if [[ ${arr[*]} =~ $(echo '\<d\>') ]]( Quelle )
Stefan van den Akker
Danke das war hilfreich. Aber wie kann ich das Ergebnis negieren, wenn ich keine andere Anhängerschaft habe? Das würde redundanten Code sparen. @Steven Penny
Sven M.
1
@SvenM. wenn [[ ! $ {arr [*]} = ~ d]]?
rwenz3l
22

1) Array initialisieren arr und fügen Sie Elemente hinzu

2) Setzen Sie die Variable, nach der gesucht werden soll SEARCH_STRING

3) Überprüfen Sie, ob Ihr Array ein Element enthält

arr=()
arr+=('a')
arr+=('b')
arr+=('c')

SEARCH_STRING='b'

if [[ " ${arr[*]} " == *"$SEARCH_STRING"* ]];
then
    echo "YES, your arr contains $SEARCH_STRING"
else
    echo "NO, your arr does not contain $SEARCH_STRING"
fi
Marcin Wasiluk
quelle
3
Dies war die einzige Antwort in dem Thread, die für mich funktioniert hat, danke!
Sishaar Rao
Dies ist fantastisch prägnant und ich konnte dies verwenden, um BASH_ARGVzu testen, ob ein bestimmtes Wort als Argument angegeben wurde, das ein anderes einmaliges Setup erfordert, ohne dass ich etwas tun musste, argparsewas für diesen Fall übertrieben ist.
Dragon788
16

Wenn Array-Elemente keine Leerzeichen enthalten, wäre eine andere (möglicherweise besser lesbare) Lösung:

if echo ${arr[@]} | grep -q -w "d"; then 
    echo "is in array"
else 
    echo "is not in array"
fi
jesjimher
quelle
+1. Gute Antwort. Mit dieser Lösung kann ich überprüfen, ob eine Nummer in einem Nummernfeld vorhanden ist. Während die Lösung von @Steven Penny in meinem Fall für das Zahlenarray nicht 100% korrekt funktionierte. if echo $ {kombinierte_p2_idx [@]} | grep -q -w $ subset_id; dann
Good Will
Ich stimme @GoodWill zu. Dies sollte die akzeptierte Antwort sein. Andere Optionen waren nicht so robust.
Grr
Es wird ein Fehler auftreten, wenn Sie negative Zahlen im Array haben
Jessi
addieren --, um das Ende der Parameter zu signalisieren, und negative Zahlen werden nicht als Parameter für grep
drAlberT behandelt.
Mit Abstand die eleganteste und verständlichste Lösung. Arbeitete wie ein Zauber für mich.
Lucas Lima
3
array=("word" "two words") # let's look for "two words"

mit grepund printf:

(printf '%s\n' "${array[@]}" | grep -x -q "two words") && <run_your_if_found_command_here>

mit for:

(for e in "${array[@]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>

Für not_found Ergebnisse hinzufügen || <run_your_if_notfound_command_here>

Qwerty
quelle
@Querty Ich weiß nicht, warum dies nicht die beste Antwort ist. grep -x...macht es am einfachsten zu bedienen.
Deck
3

Da bash nicht über einen eingebauten in Wert in Array Betreiber und dem =~Betreiber oder der [[ "${array[@]" == *"${item}"* ]]keep Notation verwirrend mich, ich in der Regel kombinieren grepmit einer hier string:

colors=('black' 'blue' 'light green')
if grep -q 'black' <<< "${colors[@]}"
then
    echo 'match'
fi

Beachten Sie jedoch, dass dies das gleiche Problem mit Fehlalarmen aufweist wie viele andere Antworten, die auftreten, wenn das zu suchende Element vollständig enthalten ist, aber nicht einem anderen Element entspricht:

if grep -q 'green' <<< "${colors[@]}"
then
    echo 'should not match, but does'
fi

Wenn dies ein Problem für Ihren Anwendungsfall ist, werden Sie wahrscheinlich nicht darum herumkommen, das Array zu durchlaufen:

for color in "${colors[@]}"
do
    if [ "${color}" = 'green' ]
    then
        echo "should not match and won't"
        break
    fi
done

for color in "${colors[@]}"
do
    if [ "${color}" = 'light green' ]
    then
        echo 'match'
        break
    fi
done
ssc
quelle
@sec Alles, was @ Quertys Antwort oben gezeigt hat, um Fehlalarme zu vermeiden, ist das Hinzufügen -xzu grep, um der gesamten Zeile zu entsprechen.
Deck
2

Hier ist eine andere Möglichkeit, die in Bezug auf die Rechenzeit möglicherweise schneller ist als das Iterieren. Nicht sicher. Die Idee ist, das Array in eine Zeichenfolge zu konvertieren, es abzuschneiden und die Größe des neuen Arrays zu ermitteln.

So finden Sie beispielsweise den Index von 'd':

arr=(a b c d)    
temp=`echo ${arr[@]}`
temp=( ${temp%%d*} )
index=${#temp[@]}

Sie könnten daraus eine Funktion machen wie:

get-index() {

    Item=$1
    Array="$2[@]"

    ArgArray=( ${!Array} )
    NewArray=( ${!Array%%${Item}*} )

    Index=${#NewArray[@]}

    [[ ${#ArgArray[@]} == ${#NewArray[@]} ]] && echo -1 || echo $Index

}

Sie könnten dann anrufen:

get-index d arr

und es würde 3 zurückgeben, was zuweisbar wäre mit:

index=`get-index d arr`
Jasonovich
quelle
0

FWIW, hier ist was ich verwendet habe:

expr "${arr[*]}" : ".*\<$item\>"

Dies funktioniert, wenn in keinem der Array-Elemente oder im Suchziel Trennzeichen vorhanden sind. Ich musste den allgemeinen Fall für meine Bewerbung nicht lösen.

Edward Falk
quelle