2 Gabel

2 Gabel

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 arraydas 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 forSchleife. Sie hat den Vorteil, dass sie keine externen Programme erfordert, unkompliziert ist und die Steuerung des genauen Ausgabeformats ohne Überraschungen relativ einfach macht.


declare -pJede Lösung, die versucht, die Ausgabe von ( ) zu verarbeiten, typeset -pmuss a) mit der Möglichkeit umgehen, dass die Variablen selbst Klammern oder eckige Klammern enthalten, und b) mit den Anführungszeichen, die declare -phinzugefü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 declaregenauer 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 forSchleife 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 %qnicht 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 forSchleife 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 KWert 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.

verwandte Informationen