Wir benötigten ein Skript, das assoziative Arrays oder eine Map-ähnliche Datenstruktur für Shell Scripting simuliert.
bash
shell
hashtable
associative-array
Irfan Zulfiqar
quelle
quelle
Eine andere Option, wenn Portabilität nicht Ihr Hauptanliegen ist, ist die Verwendung von assoziativen Arrays, die in die Shell integriert sind. Dies sollte in Bash 4.0 (jetzt verfügbar in den meisten wichtigen Distributionen, jedoch nicht in OS X, es sei denn, Sie installieren es selbst), ksh und zsh funktionieren:
Abhängig von der Shell müssen Sie möglicherweise eine
typeset -A newmap
anstelle von ausführendeclare -A newmap
, oder in einigen Fällen ist dies möglicherweise überhaupt nicht erforderlich.quelle
test -z ${variable+x}
(dasx
spielt keine Rolle, das kann eine beliebige Zeichenfolge sein). Für ein assoziatives Array in Bash können Sie ähnliche Aktionen ausführen. verwendentest -z ${map[key]+x}
.Ein weiterer Non-Bash-4-Weg.
Sie können dort auch eine if-Anweisung für die Suche eingeben. if [[$ var = ~ / blah /]]. oder Wasauchimmer.
quelle
Ich denke, Sie müssen einen Schritt zurücktreten und darüber nachdenken, was eine Karte oder ein assoziatives Array wirklich ist. Es ist lediglich eine Möglichkeit, einen Wert für einen bestimmten Schlüssel zu speichern und diesen Wert schnell und effizient zurückzugewinnen. Möglicherweise möchten Sie auch in der Lage sein, die Schlüssel zu durchlaufen, um jedes Schlüsselwertpaar abzurufen, oder Schlüssel und die zugehörigen Werte zu löschen.
Stellen Sie sich nun eine Datenstruktur vor, die Sie beim Shell-Scripting ständig verwenden, und sogar nur in der Shell, ohne ein Skript zu schreiben, das diese Eigenschaften aufweist. Stumped? Es ist das Dateisystem.
Alles, was Sie für ein assoziatives Array in der Shell-Programmierung benötigen, ist ein temporäres Verzeichnis.
mktemp -d
ist Ihr assoziativer Array-Konstruktor:Wenn Sie keine Lust haben,
echo
und zu verwendencat
, können Sie immer ein paar kleine Wrapper schreiben. Diese sind von Irfans modelliert, obwohl sie nur den Wert ausgeben, anstatt beliebige Variablen wie$value
:edit : Dieser Ansatz ist tatsächlich ziemlich viel schneller als die vom Fragesteller vorgeschlagene lineare Suche mit sed und robuster (er ermöglicht, dass Schlüssel und Werte -, =, Leerzeichen, qnd ": SP:" enthalten). Die Tatsache, dass es das Dateisystem verwendet, macht es nicht langsam; Es wird nie garantiert, dass diese Dateien auf die Festplatte geschrieben werden, es sei denn, Sie rufen an
sync
. Bei temporären Dateien wie diesen mit kurzer Lebensdauer ist es nicht unwahrscheinlich, dass viele von ihnen niemals auf die Festplatte geschrieben werden.Ich habe einige Benchmarks für Irfans Code, Jerrys Modifikation von Irfans Code und meinen Code mit dem folgenden Treiberprogramm durchgeführt:
Die Ergebnisse:
quelle
Bash4 unterstützt dies nativ. Verwenden Sie nicht
grep
odereval
, sie sind die hässlichsten Hacks.Eine ausführliche Antwort mit Beispielcode finden Sie unter: /programming/3467959
quelle
Beispiel:
quelle
Beantworten Sie nun diese Frage.
Die folgenden Skripte simulieren assoziative Arrays in Shell-Skripten. Es ist einfach und sehr leicht zu verstehen.
Map ist nichts anderes als eine nie endende Zeichenfolge, in der keyValuePair als --name = Irfan --designation = SSE --company = My: SP: Own: SP: Company gespeichert ist
Leerzeichen werden für Werte durch ': SP:' ersetzt
Bearbeiten: Fügte gerade eine andere Methode hinzu, um alle Schlüssel abzurufen.
quelle
eval
Daten so ein, als wäre es Bash-Code, und außerdem zitieren Sie sie nicht richtig. Beide verursachen eine Vielzahl von Fehlern und eine willkürliche Code-Injektion.Für Bash 3 gibt es einen speziellen Fall, der eine schöne und einfache Lösung bietet:
Wenn Sie nicht viele Variablen verarbeiten möchten oder Schlüssel einfach ungültige Variablenkennungen sind und Ihr Array garantiert weniger als 256 Elemente enthält , können Sie Funktionsrückgabewerte missbrauchen. Diese Lösung erfordert weder eine Unterschale, da der Wert als Variable verfügbar ist, noch eine Iteration, sodass die Leistung schreit. Außerdem ist es sehr gut lesbar, fast wie die Bash 4-Version.
Hier ist die grundlegendste Version:
Denken Sie daran, verwenden Sie einfache Anführungszeichen
case
, da dies sonst zu Globbing führen kann. Wirklich nützlich für statische / eingefrorene Hashes von Anfang an, aber man könnte einen Indexgenerator aus einemhash_keys=()
Array schreiben .Beachten Sie, dass standardmäßig das erste Element verwendet wird. Sie können also das nullte Element beiseite legen:
Vorsichtsmaßnahme: Die Länge ist jetzt falsch.
Wenn Sie die auf Null basierende Indizierung beibehalten möchten, können Sie alternativ einen anderen Indexwert reservieren und sich vor einem nicht vorhandenen Schlüssel schützen, der jedoch weniger lesbar ist:
Um die Länge korrekt zu halten, versetzen Sie den Index um eins:
quelle
Sie können dynamische Variablennamen verwenden und die Variablennamen wie die Schlüssel einer Hashmap arbeiten lassen.
Wenn Sie beispielsweise eine Eingabedatei mit zwei Spalten haben, Name, Gutschrift als Beispiel unten, und Sie das Einkommen jedes Benutzers summieren möchten:
Der folgende Befehl summiert alles unter Verwendung dynamischer Variablen als Schlüssel in Form von map _ $ {person} :
So lesen Sie die Ergebnisse:
Die Ausgabe wird sein:
Ich arbeite an diesen Techniken und entwickle auf GitHub eine Funktion, die genau wie ein HashMap-Objekt funktioniert , shell_map .
Um " HashMap-Instanzen " zu erstellen, kann die Funktion shell_map Kopien von sich selbst unter verschiedenen Namen erstellen. Jede neue Funktionskopie hat eine andere Variable $ FUNCNAME. $ FUNCNAME wird dann verwendet, um einen Namespace für jede Map-Instanz zu erstellen.
Die Kartenschlüssel sind globale Variablen in der Form $ FUNCNAME_DATA_ $ KEY, wobei $ KEY der der Karte hinzugefügte Schlüssel ist. Diese Variablen sind dynamische Variablen .
Unten werde ich eine vereinfachte Version davon einfügen, damit Sie als Beispiel verwenden können.
Verwendung:
quelle
Noch eine andere nicht-Bash-4-Methode (dh Bash-3, Mac-kompatibel):
Drucke:
Die Funktion mit
case
wirkt wie ein assoziatives Array. Leider kann es nicht verwendet werdenreturn
, daher muss esecho
ausgegeben werden, aber dies ist kein Problem, es sei denn, Sie sind ein Purist, der das Gabeln von Unterschalen meidet.quelle
Wie schade, dass ich die Frage vorher nicht gesehen habe - ich habe ein Library- Shell-Framework geschrieben, das unter anderem die Maps (Assoziative Arrays) enthält. Die letzte Version finden Sie hier .
Beispiel:
quelle
Hinzufügen einer weiteren Option, wenn jq verfügbar ist:
quelle
Ich habe festgestellt, dass es, wie bereits erwähnt, am besten ist, Schlüssel / Werte in eine Datei zu schreiben und sie dann mit grep / awk abzurufen. Es klingt nach allen möglichen unnötigen E / A-Vorgängen, aber der Festplatten-Cache wird aktiviert und ist äußerst effizient - viel schneller als der Versuch, sie mit einer der oben genannten Methoden im Speicher zu speichern (wie die Benchmarks zeigen).
Hier ist eine schnelle, saubere Methode, die mir gefällt:
Wenn Sie einen Einzelwert pro Schlüssel erzwingen möchten, können Sie auch eine kleine grep / sed-Aktion in hput () ausführen.
quelle
Vor einigen Jahren schrieb ich eine Skriptbibliothek für Bash, die unter anderem assoziative Arrays unterstützte (Protokollierung, Konfigurationsdateien, erweiterte Unterstützung für Befehlszeilenargumente, Hilfe generieren, Komponententests usw.). Die Bibliothek enthält einen Wrapper für assoziative Arrays und wechselt automatisch zum entsprechenden Modell (intern für bash4 und emuliert für frühere Versionen). Es hieß Shell-Framework und wurde auf origo.ethz.ch gehostet, aber heute ist die Ressource geschlossen. Wenn jemand es noch braucht, kann ich es mit Ihnen teilen.
quelle
Shell hat keine eingebaute Karte wie Datenstruktur, ich benutze rohe Zeichenfolge, um Elemente wie diese zu beschreiben:
beim Extrahieren von Elementen und ihren Attributen:
Dies scheint nicht klug zu sein als die Antwort anderer, aber für neue Leute leicht zu verstehen.
quelle
Ich habe die Lösung von Vadim wie folgt geändert:
Die Änderung betrifft map_get, um zu verhindern, dass Fehler zurückgegeben werden, wenn Sie einen Schlüssel anfordern, der nicht vorhanden ist. Der Nebeneffekt ist jedoch, dass fehlende Karten ebenfalls stillschweigend ignoriert werden, aber sie passen besser zu meinem Anwendungsfall, da ich gerade wollte nach einem Schlüssel suchen, um Elemente in einer Schleife zu überspringen.
quelle
Verspätete Antwort, aber erwägen Sie, das Problem auf diese Weise zu beheben, indem Sie die eingebaute Bash verwenden , die im Code-Snippet eines folgenden ufw-Firewall-Skripts dargestellt ist. Dieser Ansatz hat den Vorteil, dass beliebig viele begrenzte Feldsätze (nicht nur 2) verwendet werden. Wir haben die | verwendet Trennzeichen, da für Portbereichsspezifizierer möglicherweise ein Doppelpunkt erforderlich ist, z. B. 6001: 6010 .
quelle