Aus dem Python-Hintergrund kommend ist dies in der Tat eine sehr nützliche Frage!
John Jiang
5
Wenn ich mir das vier Jahre später ansehe, frage ich mich, ob es noch keinen besseren Weg gibt, dies zu tun. Oh mein Gott.
Giszmo
Fast 8 Jahre später fragte ich mich auch, ob es noch keinen besseren Weg gibt, dies zu tun. Aber diese Antwort für 2018 sieht für mich ziemlich gut aus: stackoverflow.com/a/52228219/463994
MountainX
Antworten:
86
$ for i in c,3 e,5;do IFS=",";set-- $i; echo $1 and $2;done
c and 3
e and 5
Über diese Verwendung von set(von man builtins):
Alle nach der Optionsverarbeitung verbleibenden Argumente werden als Werte für die Positionsparameter behandelt und in der Reihenfolge $ 1, $ 2, ... $ n zugewiesen
Das IFS=","setzt das Feldtrennzeichen so, dass jedes $iin $1und $2korrekt segmentiert wird.
Schön - möchte nur darauf hinweisen, IFSsollte gespeichert und auf den ursprünglichen Wert zurückgesetzt werden, wenn dies in der Befehlszeile ausgeführt wird. Außerdem kann das Neue IFSeinmal festgelegt werden, bevor die Schleife ausgeführt wird, und nicht bei jeder Iteration.
Genau
1
Falls einer der $ i mit einem Bindestrich beginnt, ist es sicherer,set -- $i
Glenn Jackman
1
Anstatt zu speichern IFS, setzen Sie es nur für den setBefehl : for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done. Bitte bearbeiten Sie Ihre Antwort: Wenn alle Leser nur eine der aufgelisteten Lösungen auswählen würden, macht es keinen Sinn, den vollständigen Entwicklungsverlauf lesen zu müssen. Danke für diesen coolen Trick!
cfi
Wenn ich deklariere tuples="a,1 b,2 c,3"und IFS=','wie in der bearbeiteten Version setze und anstatt zu c,3 e,5verwenden $tuples, druckt es überhaupt nicht gut. Wenn ich jedoch IFS=','direkt nach dem doSchlüsselwort in die for-Schleife setze , funktioniert dies sowohl bei der Verwendung $tuplesals auch bei der Verwendung von Wurfwerten. Ich dachte nur, es lohnt sich zu sagen.
Simonlbc
@Simonlbc, weil die for-Schleife IFSzum Teilen von Iterationen verwendet wird. dh wenn Sie eine Schleife über ein Array wie arr=("c,3" "e,5")und IFSvor die for-Schleife stellen, ist der Wert von $inur cund e, es wird aufgeteilt 3und wird 5daher setnicht korrekt analysiert, da $inichts zu analysieren ist. Dies bedeutet, dass, wenn die zu iterierenden Werte nicht inline sind, die Werte in IFSdie Schleife eingefügt werden sollen und der externe Wert das beabsichtigte Trennzeichen für die zu iterierende Variable berücksichtigen sollte. In den Fällen $tuplessollte es einfach sein, IFS=was Standard ist und auf Leerzeichen aufteilt.
Ungerissen
25
Ich glaube, diese Lösung ist ein wenig sauberer als die anderen, die eingereicht wurden. H / t in diesem Bash-Styleguide, um zu veranschaulichen, wie read verwendet werden kann, um Zeichenfolgen an einem Trennzeichen zu teilen und sie einzelnen Variablen zuzuweisen.
for i in c,3 e,5;do
IFS=',' read item1 item2 <<<"${i}"
echo "${item1}" and "${item2}"done
Dieser Ansatz scheint mir viel einfacher zu sein als der akzeptierte und am besten bewertete Ansatz. Gibt es einen Grund, es nicht so zu machen, im Gegensatz zu dem, was @Eduardo Ivanec vorgeschlagen hat?
spurra
@spurra Diese Antwort ist 6 Jahre und ½ jünger und basiert darauf. Gutschrift, wo es fällig ist.
Diego
1
@Diego das ist mir bewusst. Es ist ausdrücklich in der Antwort geschrieben. Ich habe gefragt, ob es einen Grund gibt, diesen Ansatz nicht gegenüber der akzeptierten Antwort zu verwenden.
spurra
2
@spurra Sie möchten Eduardos Antwort verwenden, wenn das Standardtrennzeichen (Leerzeichen, Tabulator oder Zeilenumbruch) aus irgendeinem Grund für Sie nicht funktioniert ( bash.cyberciti.biz/guide/$IFS )
Diego,
10
Verwenden Sie ein assoziatives Array (auch als Dictionary / HashMap bezeichnet):
declare -A pairs=([c]=3[e]=5)for key in"${!pairs[@]}";do
value="${pairs[$key]}"
echo "key is $key and value is $value"done
Funktioniert für bash4.0 +.
Wenn Sie Tripel anstelle von Paaren benötigen, können Sie den allgemeineren Ansatz verwenden:
animals=(dog cat mouse)
declare -A sound=([dog]=barks
[cat]=purrs
[mouse]=cheeps
)
declare -A size=([dog]=big
[cat]=medium
[mouse]=small
)for animal in"${animals[@]}";do
echo "$animal ${sound[$animal]} and it is ${size[$animal]}"done
Zu GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)Ihrer Information , dies funktionierte nicht für mich auf einem Mac mit , der über Brew installiert wurde, also YMMV.
David
es hat jedoch funktioniert GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu) ab Ubuntu 14.04 im Docker-Container.
David
scheint, als würden ältere Versionen von Bash oder solche, die diese Funktion nicht unterstützen, die Index-Basierung abarbeiten? Dabei ist der Schlüssel eher eine Zahl als eine Zeichenfolge. tldp.org/LDP/abs/html/declareref.html , und stattdessen -Ahaben wir-a .
David
David, scheint so. Ich denke, Sie können dann Array-Indizes ausprobieren, um "Assoziativität" zu erhalten. Mögendeclare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small) usw. Ich habe es noch nicht im Terminal ausprobiert, aber ich denke, es sollte funktionieren.
VasiliNovikov
7
c=('a''c')
n=(34)for i in $(seq 0 $((${#c[*]}-1)))do
echo ${c[i]} ${n[i]}done
Könnte manchmal praktischer sein.
Um den uglyTeil zu erklären , wie in den Kommentaren angegeben:
seq 0 2 erzeugt die Folge von Zahlen 0 1 2. $ (cmd) ist eine Befehlssubstitution, also für dieses Beispiel die Ausgabe von seq 0 2, die die Zahlenfolge ist. Aber was ist die Obergrenze, die $((${#c[*]}-1))?
$ ((etwas)) ist eine arithmetische Erweiterung, also ist $ ((3 + 4)) 7 usw. Unser Ausdruck ist ${#c[*]}-1also etwas - 1. Ziemlich einfach, wenn wir wissen, was ${#c[*]}ist.
c ist ein Array, c [*] ist nur das gesamte Array, $ {# c [*]} ist die Größe des Arrays, in unserem Fall 2. Jetzt rollen wir alles zurück:for i in $(seq 0 $((${#c[*]}-1))) ist for i in $(seq 0 $((2-1)))ist for i in $(seq 0 1)ist for i in 0 1. Weil das letzte Element im Array einen Index hat, der der Länge des Arrays entspricht - 1.
Sie sollten tunfor i in $(seq 0 $(($#c[*]}-1))); do [...]
reox
1
Wow, dies gewinnt die Auszeichnung „Hässlichster Haufen willkürlicher Charaktere, die ich heute gesehen habe“. Möchte jemand erklären, was genau dieser Gräuel bewirkt? Ich habe mich am Hash-Zeichen verlaufen ...
koniiiik
1
@koniiiik: Erklärung hinzugefügt.
Benutzer unbekannt
6
$ echo 'c,3;e,5;'|while IFS=',' read -d';' i j;do echo "$i and $j";done
c and 3
e and 5
Was aber, wenn das Tupel größer ist als das k / v, das ein assoziatives Array enthalten kann? Was ist, wenn es 3 oder 4 Elemente sind? Man könnte dieses Konzept erweitern:
###---------------------------------------------------### VARIABLES###---------------------------------------------------
myVars=('ya1,ya2,ya3,ya4''ye1,ye2,ye3,ye4''yo1,yo2,yo3,yo4')###---------------------------------------------------### MAIN PROGRAM###---------------------------------------------------### Echo all elements in the array###---
printf '\n\n%s\n'"Print all elements in the array..."for dataRow in"${myVars[@]}";dowhile IFS=',' read -r var1 var2 var3 var4;do
printf '%s\n'"$var1 - $var2 - $var3 - $var4"done<<<"$dataRow"done
Dann würde die Ausgabe ungefähr so aussehen:
$ ./assoc-array-tinkering.sh
Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4
Und die Anzahl der Elemente ist jetzt unbegrenzt. Keine Suche nach Stimmen; nur laut denken. REF1 , REF2
Antworten:
Über diese Verwendung von
set
(vonman builtins
):Das
IFS=","
setzt das Feldtrennzeichen so, dass jedes$i
in$1
und$2
korrekt segmentiert wird.Über diesen Blog .
Bearbeiten: korrektere Version, wie von @SLACEDIAMOND vorgeschlagen:
quelle
IFS
sollte gespeichert und auf den ursprünglichen Wert zurückgesetzt werden, wenn dies in der Befehlszeile ausgeführt wird. Außerdem kann das NeueIFS
einmal festgelegt werden, bevor die Schleife ausgeführt wird, und nicht bei jeder Iteration.set -- $i
IFS
, setzen Sie es nur für denset
Befehl :for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done
. Bitte bearbeiten Sie Ihre Antwort: Wenn alle Leser nur eine der aufgelisteten Lösungen auswählen würden, macht es keinen Sinn, den vollständigen Entwicklungsverlauf lesen zu müssen. Danke für diesen coolen Trick!tuples="a,1 b,2 c,3"
undIFS=','
wie in der bearbeiteten Version setze und anstatt zuc,3 e,5
verwenden$tuples
, druckt es überhaupt nicht gut. Wenn ich jedochIFS=','
direkt nach demdo
Schlüsselwort in die for-Schleife setze , funktioniert dies sowohl bei der Verwendung$tuples
als auch bei der Verwendung von Wurfwerten. Ich dachte nur, es lohnt sich zu sagen.IFS
zum Teilen von Iterationen verwendet wird. dh wenn Sie eine Schleife über ein Array wiearr=("c,3" "e,5")
undIFS
vor die for-Schleife stellen, ist der Wert von$i
nurc
unde
, es wird aufgeteilt3
und wird5
daherset
nicht korrekt analysiert, da$i
nichts zu analysieren ist. Dies bedeutet, dass, wenn die zu iterierenden Werte nicht inline sind, die Werte inIFS
die Schleife eingefügt werden sollen und der externe Wert das beabsichtigte Trennzeichen für die zu iterierende Variable berücksichtigen sollte. In den Fällen$tuples
sollte es einfach sein,IFS=
was Standard ist und auf Leerzeichen aufteilt.Ich glaube, diese Lösung ist ein wenig sauberer als die anderen, die eingereicht wurden. H / t in diesem Bash-Styleguide, um zu veranschaulichen, wie read verwendet werden kann, um Zeichenfolgen an einem Trennzeichen zu teilen und sie einzelnen Variablen zuzuweisen.
quelle
Basierend auf der Antwort von @ eduardo-ivanec ohne Einstellen / Zurücksetzen von
IFS
könnte man einfach Folgendes tun:Die Ausgabe:
quelle
Verwenden Sie ein assoziatives Array (auch als Dictionary / HashMap bezeichnet):
Funktioniert für bash4.0 +.
Wenn Sie Tripel anstelle von Paaren benötigen, können Sie den allgemeineren Ansatz verwenden:
quelle
GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)
Ihrer Information , dies funktionierte nicht für mich auf einem Mac mit , der über Brew installiert wurde, also YMMV.GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)
ab Ubuntu 14.04 im Docker-Container.-A
haben wir-a
.declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)
usw. Ich habe es noch nicht im Terminal ausprobiert, aber ich denke, es sollte funktionieren.Könnte manchmal praktischer sein.
Um den
ugly
Teil zu erklären , wie in den Kommentaren angegeben:seq 0 2 erzeugt die Folge von Zahlen 0 1 2. $ (cmd) ist eine Befehlssubstitution, also für dieses Beispiel die Ausgabe von
seq 0 2
, die die Zahlenfolge ist. Aber was ist die Obergrenze, die$((${#c[*]}-1))
?$ ((etwas)) ist eine arithmetische Erweiterung, also ist $ ((3 + 4)) 7 usw. Unser Ausdruck ist
${#c[*]}-1
also etwas - 1. Ziemlich einfach, wenn wir wissen, was${#c[*]}
ist.c ist ein Array, c [*] ist nur das gesamte Array, $ {# c [*]} ist die Größe des Arrays, in unserem Fall 2. Jetzt rollen wir alles zurück:
for i in $(seq 0 $((${#c[*]}-1)))
istfor i in $(seq 0 $((2-1)))
istfor i in $(seq 0 1)
istfor i in 0 1
. Weil das letzte Element im Array einen Index hat, der der Länge des Arrays entspricht - 1.quelle
for i in $(seq 0 $(($#c[*]}-1))); do [...]
quelle
Verwenden von GNU Parallel:
Oder:
Oder:
quelle
gnu parallel
brew install parallel
Verwendung
printf
in einer Prozesssubstitution:Oben erfordert
bash
. Wennbash
es nicht verwendet wird, verwenden Sie eine einfache Pipeline:quelle
beispielsweise:
quelle
Ein bisschen komplizierter, kann aber nützlich sein:
quelle
Was aber, wenn das Tupel größer ist als das k / v, das ein assoziatives Array enthalten kann? Was ist, wenn es 3 oder 4 Elemente sind? Man könnte dieses Konzept erweitern:
Dann würde die Ausgabe ungefähr so aussehen:
Und die Anzahl der Elemente ist jetzt unbegrenzt. Keine Suche nach Stimmen; nur laut denken. REF1 , REF2
quelle
In Fällen, in denen meine Tupeldefinitionen komplexer sind, bevorzuge ich sie in einem Heredoc:
Dies kombiniert das Schleifen über Zeilen eines Heredocs mit dem Teilen der Zeilen bei einem gewünschten Trennzeichen .
quelle