Was ist in Bash der einfachste Weg, um zu testen, ob ein Array einen bestimmten Wert enthält?
Bearbeiten : Mit Hilfe der Antworten und Kommentare habe ich nach einigen Tests Folgendes gefunden:
function contains() {
local n=$#
local value=${!n}
for ((i=1;i < $#;i++)) {
if [ "${!i}" == "${value}" ]; then
echo "y"
return 0
fi
}
echo "n"
return 1
}
A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
echo "contains three"
fi
Ich bin nicht sicher, ob es die beste Lösung ist, aber es scheint zu funktionieren.
[[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
if
. Ich glaube nicht, dass es einen Weg gibt, SC2199 mit diesem Ansatz zu vermeiden . Sie müssten das Array explizit durchlaufen, wie in einigen anderen Lösungen gezeigt, oder die Warnung ignorieren .Nachfolgend finden Sie eine kleine Funktion, um dies zu erreichen. Die Suchzeichenfolge ist das erste Argument und der Rest sind die Array-Elemente:
Ein Testlauf dieser Funktion könnte folgendermaßen aussehen:
quelle
"${array[@]}"
. Andernfalls beeinträchtigen Elemente, die Leerzeichen enthalten, die Funktionalität.if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi;
Vielen Dank für Ihre Hilfe!shift
verschiebt die Argumentliste um 1 nach links ( Löschen des ersten Arguments) undfor
ohnein
implizite Iteration über die Argumentliste.quelle
case "${myarray[@]}" in *"t"*) echo "found" ;; esac
Ausgaben:found
Für Saiten:
quelle
${#}
da Bash spärliche Arrays unterstützt.Einzeilige Lösung
Erläuterung
Die
printf
Anweisung druckt jedes Element des Arrays in einer separaten Zeile.Die
grep
Anweisung verwendet die Sonderzeichen^
und$
sucht nach einer Zeile, die genau das angegebene Muster enthältmypattern
(nicht mehr und nicht weniger).Verwendungszweck
Um dies in eine
if ... then
Erklärung zu setzen:Ich
-q
habe demgrep
Ausdruck ein Flag hinzugefügt , damit keine Übereinstimmungen gedruckt werden. es wird nur die Existenz eines Matches als "wahr" behandeln.quelle
Wenn Sie Leistung benötigen, möchten Sie nicht bei jeder Suche das gesamte Array durchlaufen.
In diesem Fall können Sie ein assoziatives Array (Hash-Tabelle oder Wörterbuch) erstellen, das einen Index dieses Arrays darstellt. Das heißt, es ordnet jedes Array-Element seinem Index im Array zu:
Dann können Sie es so verwenden:
Und testen Sie die Mitgliedschaft so:
Oder auch:
Beachten Sie, dass diese Lösung das Richtige tut, auch wenn der getestete Wert oder die Array-Werte Leerzeichen enthalten.
Als Bonus erhalten Sie auch den Index des Wertes innerhalb des Arrays mit:
quelle
make_index
ist aufgrund der Indirektion etwas ausgefeilter. Sie hätten einen festen Array-Namen mit einem viel einfacheren Code verwenden können.Ich benutze normalerweise nur:
Ein Wert ungleich Null zeigt an, dass eine Übereinstimmung gefunden wurde.
quelle
haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)
als -x bewirkt, dass grep versucht, die gesamte Eingabezeichenfolge abzugleichenEin weiterer Liner ohne Funktion:
Vielen Dank an @Qwerty für die Hinweise zu Leerzeichen!
entsprechende Funktion:
Beispiel:
quelle
exit 0
macht (hört so schnell wie möglich auf, wenn es gefunden wird).|| echo not found
anstelle von sein,|| not found
oder die Shell versucht, einen Befehl mit dem Namen not mit gefundenem Argument auszuführen , wenn sich der angeforderte Wert nicht im Array befindet.Behandelt jetzt leere Arrays korrekt.
quelle
"$e" = "$1"
(statt"$e" == "$1"
), der wie ein Fehler aussieht."e" == "$1"
ist syntaktisch klarer.Hier ist ein kleiner Beitrag:
Hinweis: Auf diese Weise wird der Fall "zwei Wörter" nicht unterschieden, dies ist jedoch in der Frage nicht erforderlich.
quelle
Wenn Sie einen schnellen und schmutzigen Test durchführen möchten, um festzustellen, ob es sich lohnt, das gesamte Array zu durchlaufen, um eine genaue Übereinstimmung zu erzielen, kann Bash Arrays wie Skalare behandeln. Testen Sie auf eine Übereinstimmung im Skalar. Wenn keine vorhanden ist, spart das Überspringen der Schleife Zeit. Offensichtlich können Sie falsch positive Ergebnisse erhalten.
Dies gibt "Checking" und "Match" aus. Damit
array=(word "two words" something)
wird nur "Checking" ausgegeben. Mitarray=(word "two widgets" something)
wird es keine Ausgabe geben.quelle
words
einen regulären Ausdruck ersetzen^words$
, der nur der gesamten Zeichenfolge entspricht, wodurch die Notwendigkeit, jedes Element einzeln zu überprüfen, vollständig entfällt?pattern='^words$'; if [[ ${array[@]} =~ $pattern ]]
niemals übereinstimmen wird, da das gesamte Array auf einmal überprüft wird, als wäre es ein Skalar. Die einzelnen Überprüfungen in meiner Antwort dürfen nur durchgeführt werden, wenn es einen Grund gibt, aufgrund der groben Übereinstimmung fortzufahren.Das funktioniert bei mir:
Beispielaufruf:
quelle
Wenn Sie es vorziehen, können Sie gleich lange Optionen verwenden:
quelle
Angelehnt an Dennis Williamson ‚s Antwort , die folgende Lösung kombiniert Arrays, Shell sicher zitiert, und regulären Ausdrücken die Notwendigkeit zu vermeiden: Iteration über Schleifen; Verwendung von Rohren oder anderen Teilprozessen; oder Verwenden von Nicht-Bash-Dienstprogrammen.
Der obige Code verwendet reguläre Bash-Ausdrücke, um eine Übereinstimmung mit einer stringifizierten Version des Array-Inhalts herzustellen. Es gibt sechs wichtige Schritte, um sicherzustellen, dass die Übereinstimmung mit regulären Ausdrücken nicht durch clevere Wertekombinationen innerhalb des Arrays getäuscht werden kann:
printf
Shell-Zitat%q
. Durch Shell-Anführungszeichen wird sichergestellt, dass Sonderzeichen "Shell-sicher" werden, indem sie mit einem Backslash maskiert werden\
.%q
maskiert werden. Dies ist die einzige Möglichkeit, um sicherzustellen, dass Werte innerhalb des Arrays nicht auf clevere Weise konstruiert werden können, um die Übereinstimmung mit regulären Ausdrücken zu täuschen. Ich wähle Komma,
weil dieses Zeichen am sichersten ist, wenn es auf eine ansonsten unerwartete Weise bewertet oder missbraucht wird.,,%q
als Argument verwendetprintf
. Dies ist wichtig, da zwei Instanzen des Sonderzeichens nur dann nebeneinander angezeigt werden können, wenn sie als Trennzeichen angezeigt werden. Alle anderen Instanzen des Sonderzeichens werden maskiert.${array_str}
, zu vergleichen , gegen${array_str},,
.quelle
printf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]]
vielleicht.case "$(printf ,,%q "${haystack[@]}"),," in (*"$(printf ,,%q,, "$needle")"*) true;; (*) false;; esac
Eine kleine Ergänzung zu der Antwort von @ ghostdog74 über die Verwendung von
case
Logik zum Überprüfen, ob das Array einen bestimmten Wert enthält:Oder
extglob
wenn die Option aktiviert ist, können Sie dies folgendermaßen tun:Wir können es auch mit
if
Aussage machen:quelle
gegeben :
dann eine einfache Überprüfung von:
wo
(Der Grund für die separate Zuweisung von p anstelle der Verwendung des Ausdrucks direkt in [[]] besteht darin, die Kompatibilität für Bash 4 aufrechtzuerhalten.)
quelle
Wenn Sie einige der hier vorgestellten Ideen kombinieren, können Sie eine elegante Aussage ohne Schleifen erstellen, die exakte Wortübereinstimmungen liefert .
Dies wird nicht ausgelöst
word
oderval
nur ganze Wortübereinstimmungen. Es wird unterbrochen, wenn jeder Array-Wert mehrere Wörter enthält.quelle
Ich schreibe diese Art von Dienstprogrammen im Allgemeinen, um den Namen der Variablen und nicht den Variablenwert zu verarbeiten, vor allem, weil bash Variablen sonst nicht als Referenz übergeben kann.
Hier ist eine Version, die mit dem Namen des Arrays funktioniert:
Damit wird das Fragenbeispiel:
usw.
quelle
Verwenden von
grep
undprintf
Formatieren Sie jedes Array-Element in einer neuen Zeile und dann in
Beispiel:grep
den Zeilen.Beachten Sie, dass dies keine Probleme mit Trennzeichen und Leerzeichen hat.
quelle
Einzeilige Prüfung ohne 'grep' und Schleifen
Dieser Ansatz verwendet weder externe Dienstprogramme wie
grep
noch Schleifen.Was hier passiert, ist:
IFS
Variablenwert wird.IFS
Wertersetzung vorübergehend, indem wir unseren bedingten Ausdruck in einer Unterschale (in zwei Klammern) auswerten.quelle
Parametererweiterung verwenden:
quelle
${myarray[hello]:+_}
Funktioniert hervorragend für assoziative Arrays, jedoch nicht für übliche indizierte Arrays. Bei der Frage geht es darum, einen Wert in einem Array zu finden und nicht zu überprüfen, ob der Schlüssel eines assoziativen Arrays vorhanden ist.Nachdem ich geantwortet hatte, las ich eine andere Antwort, die mir besonders gut gefallen hat, aber sie war fehlerhaft und wurde abgelehnt. Ich habe mich inspirieren lassen und hier sind zwei neue Ansätze, die ich für realisierbar halte.
mit
grep
undprintf
:mit
for
:Für not_found Ergebnisse hinzufügen
|| <run_your_if_notfound_command_here>
quelle
Hier ist meine Meinung dazu.
Ich würde lieber keine Bash-for-Schleife verwenden, wenn ich dies vermeiden kann, da die Ausführung einige Zeit in Anspruch nimmt. Wenn sich etwas wiederholen muss, lassen Sie es in einer niedrigeren Sprache als einem Shell-Skript geschrieben sein.
Dies funktioniert durch Erstellen eines temporären assoziativen Arrays,
_arr
dessen Indizes von den Werten des Eingabearrays abgeleitet werden. (Beachten Sie, dass assoziative Arrays in Bash 4 und höher verfügbar sind, sodass diese Funktion in früheren Versionen von Bash nicht funktioniert.) Wir haben festgelegt$IFS
, dass die Wortaufteilung in Leerzeichen vermieden wird.Die Funktion enthält keine expliziten Schleifen, obwohl intern Schritte durch das Eingabearray geschlagen werden, um sie zu füllen
printf
. Das printf-Format stellt%q
sicher, dass Eingabedaten maskiert werden, sodass sie sicher als Array-Schlüssel verwendet werden können.Beachten Sie, dass alles, was diese Funktion verwendet, ein integriertes Bash ist, sodass Sie auch bei der Befehlserweiterung nicht von externen Pipes nach unten gezogen werden.
Und wenn Sie nicht gerne
eval
... nun, Sie können einen anderen Ansatz verwenden. :-)quelle
eval
Anweisung am Ende der Antwort aufrufen . :)eval
(ich habe nichts dagegen, im Gegensatz zu den meisten Menschen, die weinen,eval
ist böse, meistens ohne zu verstehen, was daran böse ist). Nur dass dein Befehl gebrochen ist. Vielleicht%q
statt%s
wäre besser.eval
natürlich total bei dir ), aber du hast absolut Recht,%q
scheint zu helfen, ohne irgendetwas anderes zu beschädigen, was ich sehen kann. (Ich wusste nicht, dass% q auch eckigen Klammern entgehen würde.) Ein weiteres Problem, das ich gesehen und behoben habe, betraf Leerzeichen. Mita=(one "two " three)
, ähnlich wie bei Keegans Problem: Nicht nurarray_contains a "two "
ein falsches Negativ, sondernarray_contains a two
auch ein falsches Positiv. Einfach genug, um durch Einstellen zu reparierenIFS
.eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") )
und du kannst das fallen lassenlocal IFS=
. Es gibt immer noch ein Problem mit leeren Feldern im Array, da Bash sich weigert, einen leeren Schlüssel in einem assoziativen Array zu erstellen. Eine schnelle Möglichkeit, dies zu beheben, besteht darin, einem Dummy-Charakter ein Präfix voranzustellen, z. Bx
.:eval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )
Undreturn $(( 1 - 0${_arr[x$2]} ))
.Meine bereits vorgeschlagene Version der Technik für reguläre Ausdrücke:
Was hier passiert, ist, dass Sie das gesamte Array der unterstützten Werte in Wörter erweitern und jedem von ihnen eine bestimmte Zeichenfolge, in diesem Fall "X-", voranstellen und dasselbe mit dem angeforderten Wert tun. Wenn dieser tatsächlich im Array enthalten ist, stimmt die resultierende Zeichenfolge höchstens mit einem der resultierenden Token überein oder im Gegenteil überhaupt nicht. Im letzteren Fall ist das || Der Operator wird ausgelöst und Sie wissen, dass es sich um einen nicht unterstützten Wert handelt. Zuvor wird der angeforderte Wert durch standardmäßige Manipulation von Shell-Strings von allen führenden und nachfolgenden Leerzeichen befreit.
Ich glaube, es ist sauber und elegant, obwohl ich mir nicht sicher bin, wie leistungsfähig es sein kann, wenn Ihr Array unterstützter Werte besonders groß ist.
quelle
Hier ist meine Sicht auf dieses Problem. Hier ist die Kurzversion:
Und die lange Version, die meiner Meinung nach viel augenschonender ist.
Beispiele:
quelle
test_arr=("hello" "world" "two words")
?Ich hatte den Fall, dass ich überprüfen musste, ob eine ID in einer Liste von IDs enthalten war, die von einem anderen Skript / Befehl generiert wurden. Für mich hat folgendes funktioniert:
Sie können es auch folgendermaßen kürzen / verdichten:
In meinem Fall habe ich jq ausgeführt, um JSON nach einer Liste von IDs zu filtern, und musste später überprüfen, ob meine ID in dieser Liste enthalten war, und dies funktionierte am besten für mich. Es funktioniert nicht für manuell erstellte Arrays des Typs,
LIST=("1" "2" "4")
sondern für durch Zeilenumbrüche getrennte Skriptausgabe.PS.: Konnte keine Antwort kommentieren, da ich relativ neu bin ...
quelle
Der folgende Code prüft, ob sich ein bestimmter Wert im Array befindet, und gibt seinen auf Null basierenden Offset zurück:
Die Übereinstimmung wird mit den vollständigen Werten durchgeführt, daher würde das Setzen von VALUE = "drei" nicht übereinstimmen.
quelle
Dies könnte eine Untersuchung wert sein, wenn Sie nicht iterieren möchten:
Snippet angepasst von: http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ Ich finde es ziemlich clever.
EDIT: Sie könnten wahrscheinlich nur tun:
Letzteres funktioniert jedoch nur, wenn das Array eindeutige Werte enthält. Wenn Sie in "143" nach 1 suchen, erhalten Sie falsch positive Ergebnisse.
quelle
Ein bisschen spät, aber Sie könnten dies verwenden:
quelle
Ich habe mir dieses ausgedacht, das nur in zsh funktioniert, aber ich finde den allgemeinen Ansatz nett.
Sie nehmen Ihr Muster nur dann aus jedem Element heraus, wenn es damit beginnt
${arr[@]/#pattern/}
oder endet${arr[@]/%pattern/}
. Diese beiden Substitutionen funktionieren in Bash, aber beide gleichzeitig${arr[@]/#%pattern/}
funktionieren nur in zsh.Wenn das geänderte Array dem Original entspricht, enthält es das Element nicht.
Bearbeiten:
Dieser arbeitet in Bash:
Nach der Substitution wird die Länge beider Arrays verglichen. Wenn das Array das Element enthält, wird es durch die Ersetzung vollständig gelöscht, und die Anzahl unterscheidet sich.
quelle