Testen Sie, ob sich das Element im Array in Bash befindet

83118
Tgr

Gibt es eine gute Möglichkeit zu überprüfen, ob ein Array ein Element in bash hat (besser als durchlaufen)?

Gibt es eine andere Möglichkeit, zu überprüfen, ob eine Zahl oder eine Zeichenfolge einem Satz vordefinierter Konstanten entspricht?

14

6 Antworten auf die Frage

21
Dennis Williamson

In Bash 4 können Sie assoziative Arrays verwenden:

# 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 

Um das Array anfangs einzurichten, können Sie auch direkte Zuweisungen vornehmen:

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

oder so:

array=([foo]=1 [bar]=1 [baz]=1) 
Eigentlich funktioniert der Test [[]] nicht, wenn der Wert leer ist. ZB "array ['test'] = ''". In diesem Fall ist der Schlüssel "test" vorhanden, und Sie können ihn mit $ {! Array [@]} aufgelistet anzeigen, aber "[[$ ]]; echo $?" Echos 1, nicht 0. haridsv vor 12 Jahren 0
`$ ` ist einfach, hat aber ein Problem: Es funktioniert nicht, wenn Sie 'set -u' in Ihren Skripten verwenden (was empfohlen wird), da Sie "ungebundene Variable" erhalten würden. tokland vor 11 Jahren 1
@tokland: Wer empfiehlt es? Ich weiß es nicht. Dennis Williamson vor 11 Jahren 0
@DennisWilliamson: Ok, * einige Leute empfehlen es, aber ich denke, es wäre schön, eine Lösung zu haben, die unabhängig vom Wert dieser Flags funktioniert. tokland vor 11 Jahren 0
Einige Beiträge über `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 vor 11 Jahren 0
8
tokland

It's an old question, but I think what is the simplest solution has not appeared yet: test $. Example:

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

Outputs:

a is set b is set 

To see how this work check this.

Das Info-Handbuch empfiehlt die Verwendung von "env", um Mehrdeutigkeiten in Aliasnamen, Progs und anderen Funktionen, die möglicherweise den Namen "test" angenommen haben, zu vermeiden. Wie oben `env test $ && echo" a ist gesetzt "`. Sie können diese Funktion auch mit doppelten Klammern erhalten, desselben Tricks und dann auf null überprüfen: `[[! -z "$ "]] & & echo "b ist gesetzt" ` A.Danischewski vor 8 Jahren 2
5
Diego F. Durán

Es gibt eine Möglichkeit, zu testen, ob ein Element eines assoziativen Arrays existiert (nicht gesetzt). Dies unterscheidet sich von leer:

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

Dann benutze es:

declare -A assoc KEY="key" isNotSet assoc[$] if [ $? -ne 0 ] then echo "$ is not set." fi 
Nur eine Anmerkung: Deklarieren -A funktioniert nicht für Bash 3.2.39 (Debian Lenny), aber es funktioniert für Bash 4.1.5 (Debian Squeeze) Paweł Polewicz vor 12 Jahren 0
Assoziative Arrays wurden in Bash 4 eingeführt. Diego F. Durán vor 12 Jahren 0
Beachten Sie, dass wenn! some_check gibt dann 1` = `some_check` zurück. Also: `isNotSet () {[[...]]}`. Überprüfen Sie meine Lösung unten, Sie können es mit einer einfachen Überprüfung tun. tokland vor 11 Jahren 1
3
kane

Sie können feststellen, ob ein Eintrag vorhanden ist, indem Sie den Inhalt des Arrays an grep weiterleiten.

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

Sie können auch den Index eines Eintrags mit grep -n abrufen, der die Zeilennummer einer Übereinstimmung zurückgibt (denken Sie daran, 1 zu subtrahieren, um einen nullbasierten Index zu erhalten). Dies wird mit Ausnahme von sehr großen Arrays recht schnell sein.

# 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 

Erläuterung:

  • $( ... ) ist dasselbe wie das Verwenden von Backticks, um die Ausgabe eines Befehls in einer Variablen zu erfassen
  • printf gibt meine Daten ein Element pro Zeile aus
  • (alle Anführungszeichen zusammen mit @stattdessen *. vermeiden die Aufteilung der "Hallo Welt" in 2 Zeilen)
  • grepsucht nach exakter Zeichenfolge: ^und $stimmt mit dem Anfang und dem Zeilenende überein
  • grep -n gibt Zeile # in Form von 4: Hallo Welt zurück
  • grep -m 1 findet nur die erste Übereinstimmung
  • cut extrahiert nur die Zeilennummer
  • subtrahieren Sie 1 von der zurückgegebenen Zeilennummer.

Sie können die Subtraktion natürlich in den Befehl einklappen. Aber dann auf -1 für fehlendes testen:

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

Ich glaube nicht, dass Sie dies ohne Schleifen richtig machen können, es sei denn, Sie haben sehr begrenzte Daten im Array.

Hier ist eine einfache Variante, dies würde korrekterweise sagen, dass "Super User"es im Array existiert. Es würde aber auch sagen, dass "uper Use"das im Array ist.

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 

Das Problem ist, dass es keine einfache Möglichkeit gibt, die Anker hinzuzufügen (was mir einfällt), außer durch das Array zu schleifen. Wenn Sie sie nicht hinzufügen können, bevor Sie sie in das Array einfügen ...

Es ist jedoch eine gute Lösung, wenn die Konstanten alphanumerisch sind (mit `grep" \ b $ FINDME \ b "`). Könnte wahrscheinlich mit nicht-alphanumerischen Konstanten arbeiten, die keine Leerzeichen enthalten, mit `(^ |) $ FINDME (\ $ |)" `(oder so ähnlich ... Ich habe nie erfahren können, was für einen Geschmack von Regex Grep verwendet .) Tgr vor 13 Jahren 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 
Können Sie erläutern, warum Sie diesen Ansatz vorschlagen? OP hat gefragt, ob es eine Möglichkeit gibt *, ohne das Array * zu durchlaufen, was Sie in `in_array` tun. Prost bertieb vor 6 Jahren 1
Nun, zumindest ist diese Schleife in einer Funktion verkapselt, die für viele Fälle (mit kleinen Datensätzen) gut genug ist und kein bash 4+ erfordert. Wahrscheinlich sollte $ `verwendet werden. Tobias vor 5 Jahren 0