Was ist das Äquivalent von Python-Wörterbücher sondern in Bash (sollte unter OS X und Linux funktionieren).
+1 für declare -A
Ich kann nicht glauben, dass ich es noch nie benutzt habe! Ich habe 10 Jahre lang Bash programmiert.
Was ist das Äquivalent von Python-Wörterbücher sondern in Bash (sollte unter OS X und Linux funktionieren).
Bash 4 unterstützt diese Funktion von Haus aus. Stellen Sie sicher, dass der Hashbang Ihres Skripts #!/usr/bin/env bash
o #!/bin/bash
damit Sie nicht am Ende mit sh
. Stellen Sie sicher, dass Sie Ihr Skript entweder direkt ausführen, oder führen Sie script
con bash script
. (Nicht die tatsächliche Ausführung eines Bash-Skripts mit Bash する geschehen, und werden wirklich verwirrend!)
Sie deklarieren ein assoziatives Array, indem Sie dies tun:
declare -A animals
Sie können es mit dem normalen Array-Zuweisungsoperator mit Elementen auffüllen. Wenn Sie zum Beispiel eine Map mit folgenden Elementen haben wollen animal[sound(key)] = animal(value)
:
animals=( ["moo"]="cow" ["woof"]="dog")
Oder deklarieren und instanziieren Sie in einer Zeile:
declare -A animals=( ["moo"]="cow" ["woof"]="dog")
Dann verwenden Sie sie wie normale Arrays. verwenden
animals['key']='value'
zum Einstellen des Wertes
"${animals[@]}"
um die Werte zu erweitern
"${!animals[@]}"
(Beachten Sie die !
), um die Tasten zu erweitern
Vergessen Sie nicht, sie zu zitieren:
echo "${animals[moo]}"
for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done
Vor Bash 4 haben Sie keine assoziativen Arrays. Nicht verwenden eval
ihnen nachzueifern . Vermeiden Sie eval
wie die Pest, denn sie ist die Plage des Shell-Scripting. Der wichtigste Grund ist, dass eval
Ihre Daten als ausführbaren Code behandelt (es gibt noch viele andere Gründe).
Zuallererst : Erwägen Sie ein Upgrade auf bash 4. Das wird den ganzen Prozess für Sie viel einfacher machen.
Wenn es einen Grund gibt, warum Sie nicht aufrüsten können, declare
ist eine weitaus sicherere Option. Sie wertet Daten nicht als Bash-Code aus, wie eval
und erlaubt daher nicht so leicht die Einspeisung von beliebigem Code.
Bereiten wir die Antwort vor, indem wir die Begriffe einführen:
Erstens, die Umleitung.
$ animals_moo=cow; sound=moo; i="animals_$sound"; echo "${!i}"
cow
Zweitens, declare
:
$ sound=moo; animal=cow; declare "animals_$sound=$animal"; echo "$animals_moo"
cow
Bringen Sie sie zusammen:
# Set a value:
declare "array_$index=$value"
# Get a value:
arrayGet() {
local array=$1 index=$2
local i="${array}_$index"
printf '%s' "${!i}"
}
Benutzen wir es:
$ sound=moo
$ animal=cow
$ declare "animals_$sound=$animal"
$ arrayGet animals "$sound"
cow
Anmerkung: declare
kann nicht in eine Funktion eingefügt werden. Jede Verwendung von declare
innerhalb einer Bash-Funktion macht die von ihr erzeugte Variable . auf den Geltungsbereich dieser Funktion, was bedeutet, dass wir damit nicht auf globale Arrays zugreifen oder sie verändern können. (In Bash 4 können Sie mit declare -g
um globale Variablen zu deklarieren - aber in der Bash 4 können Sie von vornherein assoziative Arrays verwenden und so diesen Workaround vermeiden).
Zusammenfassung:
declare -A
für assoziative Arrays.declare
Option, wenn Sie nicht aktualisieren können.awk
und das Thema ganz zu vermeiden.
+1 für declare -A
Ich kann nicht glauben, dass ich es noch nie benutzt habe! Ich habe 10 Jahre lang Bash programmiert.
Ich verwende Bash 4.2, aber declare -A
beschwert sich -A
ist keine gültige Option... irgendeine Idee warum? linux distr ist SUSE...
@Richard: Vermutlich benutzen Sie nicht wirklich Bash. Ist Ihr Hashbang sh anstelle von bash, oder rufen Sie Ihren Code sonst mit sh auf? Versuchen Sie, dies direkt vor Ihr declare zu setzen: echo "$BASH_VERSION $POSIXLY_CORRECT", es sollte Folgendes ausgeben 4.x
y no y
.
Es gibt die Parametersubstitution, die allerdings auch un-PC sein kann ... wie die Indirektion.
#!/bin/bash
# Array pretending to be a Pythonic dictionary
ARRAY=( "cow:moo"
"dinosaur:roar"
"bird:chirp"
"bash:rock" )
for animal in "${ARRAY[@]}" ; do
KEY="${animal%%:*}"
VALUE="${animal##*:}"
printf "%s likes to %s.\n" "$KEY" "$VALUE"
done
printf "%s is an extinct animal which likes to %s\n" "${ARRAY[1]%%:*}" "${ARRAY[1]##*:}"
Der BASH-4-Weg ist natürlich besser, aber wenn man einen Hack braucht ... dann reicht ein Hack. Sie könnten das Array/Hash mit ähnlichen Techniken durchsuchen.
Ich würde das ändern in VALUE=${animal#*:}
um den Fall zu schützen, dass ARRAY[$x]="caesar:come:see:conquer"
Es ist auch nützlich, doppelte Anführungszeichen um ${ARRAY[@]} zu setzen, falls Leerzeichen in den Schlüsseln oder Werten vorkommen, wie in for animal in "${ARRAY[@]}"; do
Aber ist die Effizienz nicht ziemlich schlecht? Ich denke an O(n*m), wenn man mit einer anderen Liste von Schlüsseln vergleichen will, statt an O(n) mit richtigen Hashmaps (konstante Suchzeit, O(1) für einen einzelnen Schlüssel).
Das ist es, was ich hier gesucht habe:
declare -A hashmap
hashmap["key"]="value"
hashmap["key2"]="value2"
echo "${hashmap["key"]}"
for key in ${!hashmap[@]}; do echo $key; done
for value in ${hashmap[@]}; do echo $value; done
echo hashmap has ${#hashmap[@]} elements
Dies funktionierte bei mir nicht mit Bash 4.1.5:
animals=( ["moo"]="cow" )
Beachten Sie, dass der Wert keine Leerzeichen enthalten darf, sonst fügen Sie mehrere Elemente auf einmal hinzu
Upvote für die Syntax hashmap["key"]="value", die auch ich in der ansonsten fantastisch akzeptierten Antwort vermisst habe.
@rubo77 Schlüssel auch nicht, es werden mehrere Schlüssel hinzugefügt. Gibt es eine Möglichkeit, dies zu umgehen?
Das Dateisystem ist eine Baumstruktur, die als Hash-Map verwendet werden kann. Ihre Hash-Tabelle ist ein temporäres Verzeichnis, Ihre Schlüssel sind Dateinamen und Ihre Werte sind Dateiinhalte. Der Vorteil ist, dass es große Hashmaps verarbeiten kann und keine spezielle Shell benötigt.
hashtable=$(mktemp -d)
echo $value > "$hashtable/$key"
value=$(< "$hashtable/$key")
Natürlich ist es langsam, aber nicht dass langsam. Ich habe es auf meinem Rechner getestet, mit einer SSD und btrfs und zwar um 3000 Elemente lesen/schreiben pro Sekunde .
Welche Version der Bash unterstützt mkdir -d
? (Nicht 4.3, auf Ubuntu 14. Ich würde auf mkdir /run/shm/foo
oder ob dadurch der Arbeitsspeicher gefüllt wird, mkdir /tmp/foo
.)
Was ist der Unterschied zwischen $value=$(< $hashtable/$key)
y value=$(< $hashtable/$key)
? Danke!
Sie können die hput()/hget()-Schnittstelle weiter modifizieren, so dass Sie wie folgt benannte Hashes haben:
hput() {
eval "$1""$2"='$3'
}
hget() {
eval echo '${'"$1$2"'#hash}'
}
und dann
hput capitals France Paris
hput capitals Netherlands Amsterdam
hput capitals Spain Madrid
echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain`
So können Sie andere Karten definieren, die nicht in Konflikt stehen (z.B. 'rcapitals', das Länder nach Hauptstädten sucht). Aber wie auch immer, ich denke, Sie werden feststellen, dass dies alles ziemlich schrecklich ist, was die Leistung angeht.
Wenn Sie wirklich schnell Hash Lookup wollen, gibt es eine schreckliche, schreckliche hack, die tatsächlich funktioniert wirklich gut. Es ist dies: Schreiben Sie Ihre Schlüssel/Werte in eine temporäre Datei, eine pro Zeile, dann verwenden Sie 'grep "^$key"', um sie herauszuholen, und verwenden Sie Pipes mit cut oder awk oder sed oder was auch immer, um die Werte abzurufen.
Wie gesagt, es hört sich schrecklich an, und es hört sich an, als müsste es langsam sein und alle möglichen unnötigen IO ausführen, aber in der Praxis ist es sehr schnell (Festplattencache ist genial, nicht wahr?), sogar für sehr große Hash-Tabellen. Sie müssen die Einzigartigkeit der Schlüssel selbst erzwingen, usw. Selbst wenn Sie nur ein paar hundert Einträge haben, wird die Ausgabedatei/Grep-Kombination um einiges schneller sein - meiner Erfahrung nach um ein Vielfaches. Außerdem verbraucht sie weniger Speicher.
Hier ist eine Möglichkeit, dies zu tun:
hinit() {
rm -f /tmp/hashmap.$1
}
hput() {
echo "$2 $3" >> /tmp/hashmap.$1
}
hget() {
grep "^$2 " /tmp/hashmap.$1 | awk '{ print $2 };'
}
hinit capitals
hput capitals France Paris
hput capitals Netherlands Amsterdam
hput capitals Spain Madrid
echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain`
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
6 Stimmen
Bash soll ein Python/Perl-Skript ausführen... Das ist so viel flexibler!
1 Stimmen
Siehe auch: Assoziative Arrays in Shell-Skripten