![2 Gabel](https://rvso.com/image/112013/2%20Gabel.png)
Gibt es eine Möglichkeit, ein ganzes Array ([Schlüssel]=Wert) zu drucken, ohne eine Schleife über alle Elemente auszuführen?
Angenommen, ich habe ein Array mit einigen Elementen erstellt:
declare -A array
array=([a1]=1 [a2]=2 ... [b1]=bbb ... [f500]=abcdef)
Ich kann das gesamte Array ausdrucken mit
for i in "${!array[@]}"
do
echo "${i}=${array[$i]}"
done
${!array[@]}
Es scheint jedoch, dass Bash bereits weiß, wie alle Array-Elemente – sowohl Schlüssel als auch Werte – in einem Durchgang abgerufen werden können ${array[@]}
.
Gibt es eine Möglichkeit, Bash diese Informationen ohne die Schleife drucken zu lassen?
Bearbeiten:
typeset -p array
das tut es!
Allerdings kann ich Präfix und Suffix nicht in einer einzigen Ersetzung entfernen:
a="$(typeset -p array)"
b="${a##*(}"
c="${b%% )*}"
Gibt es eine sauberere Möglichkeit, nur den Schlüssel=Wert-Teil der Ausgabe abzurufen/drucken?
Antwort1
Ich glaube, Sie fragen da zwei verschiedene Dinge.
Gibt es eine Möglichkeit, Bash diese Informationen ohne die Schleife drucken zu lassen?
Ja, aber sie sind nicht so gut, wie wenn man einfach die Schleife verwendet.
Gibt es eine sauberere Möglichkeit, nur den Schlüssel=Wert-Teil der Ausgabe abzurufen/drucken?
Ja, die for
Schleife. Sie hat den Vorteil, dass sie keine externen Programme erfordert, unkompliziert ist und die Steuerung des genauen Ausgabeformats ohne Überraschungen relativ einfach macht.
declare -p
Jede Lösung, die versucht, die Ausgabe von ( ) zu verarbeiten, typeset -p
muss a) mit der Möglichkeit umgehen, dass die Variablen selbst Klammern oder eckige Klammern enthalten, und b) mit den Anführungszeichen, die declare -p
hinzugefügt werden müssen, damit die Ausgabe eine gültige Eingabe für die Shell ist.
Beispielsweise b="${a##*(}"
frisst Ihre Erweiterung einige der Werte, wenn ein Schlüssel/Wert eine öffnende Klammer enthält. Das liegt daran, dass Sie verwendet haben ##
, wodurch dieam längstenPräfix. Dasselbe gilt für c="${b%% )*}"
. Obwohl Sie natürlich den von ausgegebenen Standardtext declare
genauer anpassen könnten, wäre es trotzdem schwierig, wenn Sie nicht all die Anführungszeichen wollten, die es macht.
Das sieht nicht sehr schön aus, wenn man es nicht braucht.
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
Mit der for
Schleife ist es einfacher, das Ausgabeformat nach Belieben auszuwählen:
# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'
# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'
Von dort aus ist es aucheinfachum das Ausgabeformat anderweitig zu ändern (entfernen Sie die Klammern um den Schlüssel, setzen Sie alle Schlüssel/Wert-Paare in eine einzelne Zeile …). Wenn Sie Anführungszeichen für etwas anderes als die Shell selbst benötigen, müssen Sie dies trotzdem selbst tun, aber Sie haben zumindest die Rohdaten, mit denen Sie arbeiten können. (Wenn Sie Zeilenumbrüche in den Schlüsseln oder Werten haben, werden Sie wahrscheinlich einige Anführungszeichen benötigen.)
Mit einer aktuellen Bash (4.4, glaube ich) können Sie auch printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
anstelle von verwenden printf "%q=%q"
. Dadurch wird ein etwas schöneres Format in Anführungszeichen erzeugt, aber es ist natürlich etwas aufwändiger, daran zu denken. (Und es zitiert den Sonderfall von @
as array key, der %q
nicht zitiert.)
Wenn das Schreiben der For-Schleife zu aufwändig erscheint, speichern Sie sie irgendwo als Funktion (hier ohne Anführungszeichen):
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
Und dann verwenden Sie einfach das:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
Funktioniert auch mit indizierten Arrays:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
Antwort2
declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'
2 Gabel
Vielleicht das:
printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1
printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb
printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2 2
a1 1
f50 abcd
zz Hello World
b1 bbb
3 Gabeln
oder dieses:
paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
Keine Gabel
verglichen werden mit
for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
Ausführungszeitenvergleich
Da die letzte Syntax kein Fork verwendet, könnte sie schneller sein:
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
5 11 76
real 0m0.005s
user 0m0.000s
sys 0m0.000s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
5 6 41
real 0m0.008s
user 0m0.000s
sys 0m0.000s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
5 6 41
real 0m0.002s
user 0m0.000s
sys 0m0.001s
Diese Aussage gilt jedoch nicht, wenn das Array groß wird. Wenn die Reduzierung der Forks für kleine Prozesse effizient ist, ist die Verwendung dedizierter Tools für größere Prozesse effizienter.
for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
17581 35163 292941
real 0m0.150s
user 0m0.124s
sys 0m0.036s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
17581 17582 169875
real 0m0.140s
user 0m0.000s
sys 0m0.004s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
17581 17582 169875
real 0m0.312s
user 0m0.268s
sys 0m0.076s
Anmerkung
Wie beide (gegabelt) Lösungen verwendenAusrichtung, keine davon funktioniert, wenn eine Variable einenNeue ZeileIn diesem Fall ist eine for
Schleife der einzige Weg.
Robustere und ausführlichere Antwort bei StackOverflow
Antwort3
Bash 5.1 bietet eine sehr direkte Möglichkeit, assoziative Arrays anzuzeigen, indem der K
Wert wie folgt verwendet wird ${arr[@]@K}
:
$ declare -A arr
$ arr=(k1 v1 k2 v2)
$ printf "%s\n" "${arr[@]@K}"
k1 "v1" k2 "v2"
Von demBash 5.1-Beschreibungsdokument:
hh. Neue „K“-Parametertransformation zur Anzeige assoziativer Arrays als Schlüssel-Wert-Paare.
Es ist gut erklärt in derBash-Referenzhandbuch → 3.5.3 Shell-Parametererweiterung:
${parameter@operator}
K
Erzeugt eine möglicherweise in Anführungszeichen gesetzte Version des Parameterwerts, mit der Ausnahme, dass die Werte indizierter und assoziativer Arrays als Folge in Anführungszeichen gesetzter Schlüssel-Wert-Paare ausgegeben werden (siehe Arrays).
Antwort4
Seitsetzentut, was Sie wollen, warum bearbeiten Sie nicht einfach die Ausgabe?
typeset -p array | sed s/^.*\(// | tr -d ")\'\"" | tr "[" "\n" | sed s/]=/' = '/
gibt
a2 = 2
a1 = 1
b1 = bbb
Wo
array='([a2]="2" [a1]="1" [b1]="bbb" )'
Ausführlich, aber es ist ziemlich einfach zu sehen, wie die Formatierung funktioniert: Führen Sie einfach die Pipeline mit zunehmend mehr dersedUndtrBefehle. Ändern Sie sie, um sie an den Geschmack des hübschen Druckens anzupassen.