Assoziative Arrays in Shell-Skripten

10

Ich habe einen Trick zum Implementieren von assoziativen Arrays in einem Shell-Skript gesehen. Zum Beispiel print array["apples"]könnte geschrieben werden, echo \$array$keywo key = Äpfel.

Es wurde jedoch nicht erwähnt, wie die Schlüssel generiert werden sollen, um über das Array zu iterieren. Die einzige Möglichkeit, die ich mir vorstellen konnte, bestand darin, die Schlüssel in einer durch Leerzeichen getrennten Variablen zu speichern, damit ich eine for-Schleife verwenden konnte, um über das Array zu iterieren.

Gibt es eine andere Möglichkeit, die Schlüssel für die spätere Verwendung aufzubewahren?

Egghead
quelle
5
Wenn Sie versuchen, assoziative Arrays in einem Shell-Skript zu verwenden, ist Ihr Projekt möglicherweise zu komplex für ein Shell-Skript :)
Martin von Wittich
@ MartinvonWittich warum? Ich habe ein Shell-Skript, das ein SQL-Skript auf einem von 3 möglichen DB-Schemas ausführt. Das erforderliche Schema ist im Dateinamen mit einer Abkürzung enthalten. Ich benötige eine Zuordnung zwischen dieser Abkürzung und dem tatsächlichen Schemanamen. Was gibt es Schöneres als ein assoziatives Array, wenn man bedenkt, dass die tatsächlichen Schemanamen (nicht die Abkürzung) zwischen den Umgebungen unterschiedlich sind, sodass eine Array-Variable (deren Werte nur einmal festgelegt werden können) perfekt ist
Slav
2
@Slav Ich argumentiere nicht gegen assoziative Arrays, sondern nur gegen Shell-Skripte, bei denen eine solche Komplexität erforderlich ist. Aber das ist nur meine persönliche Präferenz; Ich fange oft an, ein Shell-Skript zu schreiben und es dann sofort in Perl neu zu schreiben, wenn ich merke, dass ich eine bestimmte Komplexitätsschwelle überschreite.
Martin von Wittich

Antworten:

19

Shells mit assoziativen Arrays

Einige moderne Shells bieten assoziative Arrays: ksh93, bash ≥4, zsh. Wenn aes sich in ksh93 und bash um ein assoziatives Array handelt, "${!a[@]}"handelt es sich um das Array seiner Schlüssel:

for k in "${!a[@]}"; do
  echo "$k -> ${a[$k]}"
done

In zsh funktioniert diese Syntax nur im ksh-Emulationsmodus. Andernfalls müssen Sie die native Syntax von zsh verwenden:

for k in "${(@k)a}"; do
  echo "$k -> $a[$k]"
done

${(k)a}funktioniert auch, wenn akein leerer Schlüssel vorhanden ist.

In zsh können Sie auch gleichzeitig kEys und vAlues durchlaufen:

for k v ("${(@kv)a}") echo "$k -> $v"

Shells ohne assoziative Arrays

Das Emulieren von assoziativen Arrays in Shells, die sie nicht haben, ist viel mehr Arbeit. Wenn Sie assoziative Arrays benötigen, ist es wahrscheinlich an der Zeit, ein größeres Tool wie ksh93 oder Perl einzuführen.

Wenn Sie assoziative Arrays in einer reinen POSIX-Shell benötigen, können Sie diese auf folgende Weise simulieren, wenn die Schlüssel nur die Zeichen 0-9A-Z_a-z(ASCII-Ziffern, Buchstaben und Unterstriche) enthalten. Unter dieser Annahme können Schlüssel als Teil von Variablennamen verwendet werden. Die folgenden Funktionen wirken auf ein Array, das durch ein Namenspräfix, den „Stamm“, gekennzeichnet ist, der keine zwei aufeinander folgenden Unterstriche enthalten darf.

## ainit STEM
## Declare an empty associative array named STEM.
ainit () {
  eval "__aa__${1}=' '"
}
## akeys STEM
## List the keys in the associatve array named STEM.
akeys () {
  eval "echo \"\$__aa__${1}\""
}
## aget STEM KEY VAR
## Set VAR to the value of KEY in the associative array named STEM.
## If KEY is not present, unset VAR.
aget () {
  eval "unset $3
        case \$__aa__${1} in
          *\" $2 \"*) $3=\$__aa__${1}__$2;;
        esac"
}
## aset STEM KEY VALUE
## Set KEY to VALUE in the associative array named STEM.
aset () {
  eval "__aa__${1}__${2}=\$3
        case \$__aa__${1} in
          *\" $2 \"*) :;;
          *) __aa__${1}=\"\${__aa__${1}}$2 \";;
        esac"
}
## aunset STEM KEY
## Remove KEY from the associative array named STEM.
aunset () {
  eval "unset __aa__${1}__${2}
        case \$__aa__${1} in
          *\" $2 \"*) __aa__${1}=\"\${__aa__${1}%%* $2 } \${__aa__${1}#* $2 }\";;
        esac"
}

(Warnung, nicht getesteter Code. Fehlererkennung für syntaktisch ungültige Stämme und Schlüssel wird nicht bereitgestellt.)

Gilles 'SO - hör auf böse zu sein'
quelle
5

Ich bin nicht sicher, was Sie unter "speichern" verstehen, aber Sie können die Schlüssel mithilfe der folgenden ${!array[@]}Syntax durchlaufen :

$ typeset -A foo=([key1]=bar [key2]=baz);
$ echo "${!foo[@]}" 
key2 key1

Also, um zu wiederholen:

$ for key in "${!foo[@]}"; do echo "$key : ${foo[$key]}"; done
key2 : baz
key1 : bar

Ich habe hier ein schönes, kurzes Tutorial dazu gefunden .


Wie in den Kommentaren unten ausgeführt, wurden in bashVersion 4 assoziative Arrays hinzugefügt . Hier finden Sie einen Artikel in einem Linux-Journal zu diesem Thema.

terdon
quelle
1
(bash version 4 only)Das ist wichtig zu beachten. Traditionell sind bashArrays nur numerisch.
Ricky Beam
1
Möglicherweise möchten Sie typesetstatt declarein Ihren Beispielen verwenden. Das würde sie zwischen bash 4 und ksh93 portabel machen, die zuerst Shell-assoziative Arrays implementierten.
Jlliagre
0

Shells ohne assoziative Arrays

Es ist nicht so schwer, wenn die Tasten auf [0-9A-Za-z_](Zahlen, Buchstaben, Unterstrich) beschränkt sind.

Der Trick besteht darin, anstatt in Array [ $ key ] zu speichern, in Variablen array_ $ key zu speichern .

Einstellen:

eval "array_$key='$value'"

Erhalten:

value=`eval echo '$'array_$key`

Hinweis: Werte dürfen nicht enthalten '(einfaches Anführungszeichen).

Marián Černý
quelle
-1

Das funktioniert in Bash

cert="first"
web="second"
declare -A assoc_array=(["cert"]="${cert}" ["web"]="${web}")
echo "first is" ${assoc_array[cert]}
echo "second is" ${assoc_array[web]}

ODER

#loop
for i in "${assoc_array[@]}"
do
   echo "$i"
done

Keine Notwendigkeit, eval afaik zu verwenden

JamesD
quelle
Ich glaube, dass Sie den Punkt der Frage verpasst haben.
G-Man sagt "Reinstate Monica"