823 Stimmen

Wie definiert man Hash-Tabellen in der Bash?

Was ist das Äquivalent von Python-Wörterbücher sondern in Bash (sollte unter OS X und Linux funktionieren).

6 Stimmen

Bash soll ein Python/Perl-Skript ausführen... Das ist so viel flexibler!

1 Stimmen

24voto

AsymLabs Punkte 863

Erwägen Sie eine Lösung mit dem Bash-Builtin lesen wie der folgende Codeausschnitt aus einem ufw-Firewall-Skript zeigt. Dieser Ansatz hat den Vorteil, dass beliebig viele abgegrenzte Feldsätze (nicht nur 2) verwendet werden können. Wir haben die | Trennzeichen, da Portbereichsspezifikationen einen Doppelpunkt erfordern können, d.h. 6001:6010 .

#!/usr/bin/env bash

readonly connections=(       
                            '192.168.1.4/24|tcp|22'
                            '192.168.1.4/24|tcp|53'
                            '192.168.1.4/24|tcp|80'
                            '192.168.1.4/24|tcp|139'
                            '192.168.1.4/24|tcp|443'
                            '192.168.1.4/24|tcp|445'
                            '192.168.1.4/24|tcp|631'
                            '192.168.1.4/24|tcp|5901'
                            '192.168.1.4/24|tcp|6566'
)

function set_connections(){
    local range proto port
    for fields in ${connections[@]}
    do
            IFS=$'|' read -r range proto port <<< "$fields"
            ufw allow from "$range" proto "$proto" to any port "$port"
    done
}

set_connections

4 Stimmen

@CharlieMartin : read ist eine sehr mächtige Funktion und wird von vielen Bash-Programmierern nicht ausreichend genutzt. Es erlaubt kompakte Formen von Lisp-ähnlich Listenverarbeitung. Im obigen Beispiel können wir zum Beispiel nur das erste Element entfernen und den Rest beibehalten (d.h. ein ähnliches Konzept wie erste y Rest in Lisp) zu tun: IFS=$'|' read -r first rest <<< "$fields"

20voto

DigitalRoss Punkte 138823
hput () {
  eval hash"$1"='$2'
}

hget () {
  eval echo '${hash'"$1"'#hash}'
}
hput France Paris
hput Netherlands Amsterdam
hput Spain Madrid
echo `hget France` and `hget Netherlands` and `hget Spain`

$ sh hash.sh
Paris and Amsterdam and Madrid

32 Stimmen

Seufz, das scheint unnötig beleidigend und ist ohnehin ungenau. Man würde die Eingabevalidierung, das Escaping oder die Kodierung (sehen Sie, ich weiß es tatsächlich) nicht in die Eingeweide der Hash-Tabelle packen, sondern eher in einen Wrapper und so schnell wie möglich nach der Eingabe.

0 Stimmen

@DigitalRoss können Sie erklären, was die Verwendung von #hash in eval echo '${hash'"$1"'#hash}' . für mich scheint es mir als Kommentar nicht mehr als das zu sein. hat #hash hier eine besondere Bedeutung?

0 Stimmen

@Sanjay ${var#start} entfernt den Text Start vom Beginn des in der Variablen gespeicherten Wertes an var .

8voto

marco Punkte 420

Ich stimme @lhunath und anderen zu, dass die assoziative Array sind der Weg zu gehen mit Bash 4. Wenn Sie an Bash 3 festhalten (OSX, alte Distros, die Sie nicht aktualisieren können), können Sie auch expr verwenden, das überall sein sollte, einen String und reguläre Ausdrücke. Ich mag es besonders, wenn das Wörterbuch nicht zu groß ist.

  1. Wählen Sie 2 Trennzeichen, die Sie nicht in Schlüsseln und Werten verwenden werden (z. B. ',' und ':' )

  2. Schreiben Sie Ihre Karte als String (beachten Sie auch das Trennzeichen ',' am Anfang und Ende)

    animals=",moo:cow,woof:dog,"
  3. Verwenden Sie eine Regex, um die Werte zu extrahieren

    get_animal {
        echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")"
    }
  4. Die Zeichenkette aufteilen, um die Elemente aufzulisten

    get_animal_items {
        arr=$(echo "${animals:1:${#animals}-2}" | tr "," "\n")
        for i in $arr
        do
            value="${i##*:}"
            key="${i%%:*}"
            echo "${value} likes to $key"
        done
    }

Jetzt können Sie es verwenden:

$ animal = get_animal "moo"
cow
$ get_animal_items
cow likes to moo
dog likes to woof

6voto

Cole Stanfield Punkte 2397

Die Antwort von Al P. gefiel mir sehr gut, aber ich wollte Einzigartigkeit auf billige Weise erzwingen, also ging ich noch einen Schritt weiter und verwendete ein Verzeichnis. Es gibt einige offensichtliche Einschränkungen (Verzeichnisdateilimits, ungültige Dateinamen), aber es sollte für die meisten Fälle funktionieren.

hinit() {
    rm -rf /tmp/hashmap.$1
    mkdir -p /tmp/hashmap.$1
}

hput() {
    printf "$3" > /tmp/hashmap.$1/$2
}

hget() {
    cat /tmp/hashmap.$1/$2
}

hkeys() {
    ls -1 /tmp/hashmap.$1
}

hdestroy() {
    rm -rf /tmp/hashmap.$1
}

hinit ids

for (( i = 0; i < 10000; i++ )); do
    hput ids "key$i" "value$i"
done

for (( i = 0; i < 10000; i++ )); do
    printf '%s\n' $(hget ids "key$i") > /dev/null
done

hdestroy ids

In meinen Tests schneidet es auch etwas besser ab.

$ time bash hash.sh 
real    0m46.500s
user    0m16.767s
sys     0m51.473s

$ time bash dirhash.sh 
real    0m35.875s
user    0m8.002s
sys     0m24.666s

Ich dachte nur, ich würde mich einmischen. Prost!

Bearbeiten: Hinzufügen von hdestroy()

5voto

Adam Katz Punkte 12112

Ein Kollege hat gerade diesen Thread erwähnt. Ich habe unabhängig Hash-Tabellen in der Bash implementiert, und es ist nicht abhängig von Version 4. Aus einem Blogbeitrag von mir im März 2010 (vor einigen der Antworten hier...) mit dem Titel Hash-Tabellen in der Bash :

I zuvor gebraucht cksum zu Hash, haben aber inzwischen übersetzt Javas Zeichenfolge hashCode zu nativer bash/zsh.

# Here's the hashing function
ht() {
  local h=0 i
  for (( i=0; i < ${#1}; i++ )); do
    let "h=( (h<<5) - h ) + $(printf %d \'${1:$i:1})"
    let "h |= h"
  done
  printf "$h"
}

# Example:

myhash[`ht foo bar`]="a value"
myhash[`ht baz baf`]="b value"

echo ${myhash[`ht baz baf`]} # "b value"
echo ${myhash[@]} # "a value b value" though perhaps reversed
echo ${#myhash[@]} # "2" - there are two values (note, zsh doesn't count right)

Es ist nicht bidirektional, und der eingebaute Weg ist viel besser, aber beides sollte nicht wirklich verwendet werden. Die Bash ist für schnelle, einmalige Aufgaben gedacht, und solche Dinge sollten nur sehr selten eine Komplexität aufweisen, die Hashes erfordert, außer vielleicht in Ihrem ~/.bashrc und Freunde.

0 Stimmen

Der Link in der Antwort ist erschreckend! Wenn Sie ihn anklicken, bleiben Sie in einer Umleitungsschleife stecken. Bitte aktualisieren.

1 Stimmen

@MohammadRakibAmin - Ja, meine Website ist nicht mehr verfügbar, und ich bezweifle, dass ich meinen Blog wieder aufleben lassen werde. Ich habe den obigen Link zu einer archivierten Version aktualisiert. Vielen Dank für Ihr Interesse!

0 Stimmen

Es sieht nicht so aus, als ob dies mit Hash-Kollisionen umgehen würde.

CodeJaeger.com

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.

Powered by:

X