Wenn ich ein Array wie dieses in Bash habe:
FOO=( a b c )
Wie verbinde ich die Elemente mit Kommas? Zum Beispiel produzieren a,b,c
.
Umschreiblösung von Pascal Pilz als Funktion in 100% reinem Bash (keine externen Befehle):
function join_by { local IFS="$1"; shift; echo "$*"; }
Zum Beispiel,
join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c
Alternativ können wir printf verwenden, um Trennzeichen mit mehreren Zeichen zu unterstützen, wobei die Idee von @gniourf_gniourf verwendet wird
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
Zum Beispiel,
join_by , a b c #a,b,c
join_by ' , ' a b c #a , b , c
join_by ')|(' a b c #a)|(b)|(c
join_by ' %s ' a b c #a %s b %s c
join_by $'\n' a b c #a<newline>b<newline>c
join_by - a b c #a-b-c
join_by '\' a b c #a\b\c
konsolebox
Stil :)function join { local IFS=$1; __="${*:2}"; }
oderfunction join { IFS=$1 eval '__="${*:2}"'; }
. Dann__
nach verwenden. Ja, ich bin derjenige, der die Verwendung__
als Ergebnisvariable fördert ;) (und eine allgemeine Iterationsvariable oder temporäre Variable). Wenn das Konzept auf eine beliebte Bash-Wiki-Site$d
in den Formatbezeichner von einprintf
. Sie denken, Sie sind in Sicherheit, da Sie dem entkommen sind,%
aber es gibt noch andere Einschränkungen: Wenn das Trennzeichen einen Backslash enthält (z. B.\n
) oder wenn das Trennzeichen mit einem Bindestrich beginnt (und vielleicht andere, an die ich jetzt nicht denken kann). Sie können diese natürlich beheben (Backslashes durch doppelte Backslashes ersetzen und verwendenprintf -- "$d%s"
), aber irgendwann werden Sie das Gefühl haben, gegen die Shell zu kämpfen, anstatt damit zu arbeiten. Aus diesem Grund habe ich in meiner Antwort unten den Trennzeichen den zu verbindenden Begriffen vorangestellt.Noch eine andere Lösung:
Bearbeiten: gleich, jedoch für ein mehrstelliges Trennzeichen mit variabler Länge:
quelle
printf -v bar ",%s" "${foo[@]}"
. Es ist einsfork
weniger (eigentlichclone
). Es wird sogar gegabelt, eine Datei zu lesen :printf -v bar ",%s" $(<infile)
.$separator
enthält nicht%s
oder so, können Sie Ihreprintf
robuste machen :printf "%s%s" "$separator" "${foo[@]}"
.printf "%s%s"
verwenden, wird in der ersten Instanz NUR ein Ausgabesatz verwendet, und dann werden die restlichen Argumente einfach verkettet.printf "%s" "${foo[@]/#/$separator}"
.IFS=; regex="${foo[*]/#/$separator}"
. An diesem Punkt wird dies im Wesentlichen zur Antwort von gniourf_gniourf, die IMO von Anfang an sauberer ist, dh die Funktion verwendet, um den Umfang von IFS-Änderungen und temporären Variablen zu begrenzen.quelle
bar=$( IFS=, ; echo "${foo[*]}" )
@
anstelle von*
, wie in$(IFS=, ; echo "${foo[@]}")
? Ich kann sehen, dass das*
Leerzeichen in den Elementen bereits erhalten bleibt, auch hier nicht sicher, wie, da dies@
normalerweise erforderlich ist.*
. Suchen Sie in der Bash-Manpage nach "Spezielle Parameter" und suchen Sie nach der Erklärung neben*
:Vielleicht zB
quelle
echo "-${IFS}-"
(die geschweiften Klammern trennen die Striche vom Variablennamen).echo $IFS
Überraschenderweise ist meine Lösung noch nicht gegeben :) Dies ist der einfachste Weg für mich. Es braucht keine Funktion:
Hinweis: Es wurde beobachtet, dass diese Lösung im Nicht-POSIX-Modus gut funktioniert. Im POSIX-Modus sind die Elemente weiterhin ordnungsgemäß verbunden, werden jedoch
IFS=,
dauerhaft.quelle
Hier ist eine 100% reine Bash-Funktion, die den Job erledigt:
Aussehen:
Dadurch bleiben auch die nachfolgenden Zeilenumbrüche erhalten, und es ist keine Unterschale erforderlich, um das Ergebnis der Funktion zu erhalten. Wenn Ihnen das nicht gefällt
printf -v
(warum würde es Ihnen nicht gefallen?) Und Sie einen Variablennamen übergeben, können Sie natürlich eine globale Variable für die zurückgegebene Zeichenfolge verwenden:quelle
join_ret
eine lokale Variable erstellen und diese am Ende wiederholen. Dies ermöglicht die Verwendung von join () auf die übliche Art$(join ":" one two three)
und Weise der Shell-Skripterstellung, z. B. und erfordert keine globale Variable.$(...)
schneidet nach Zeilenumbrüchen; Wenn das letzte Feld des Arrays nachfolgende Zeilenumbrüche enthält, werden diese gekürzt (siehe Demo, in der sie nicht mit meinem Design gekürzt werden).Dies unterscheidet sich nicht allzu sehr von vorhandenen Lösungen, vermeidet jedoch die Verwendung einer separaten Funktion, ändert sich nicht
IFS
in der übergeordneten Shell und befindet sich in einer einzigen Zeile:ergebend
Einschränkung: Das Trennzeichen darf nicht länger als ein Zeichen sein.
quelle
Keine externen Befehle verwenden:
Warnung: Es wird davon ausgegangen, dass Elemente keine Leerzeichen haben.
quelle
echo ${FOO[@]} | tr ' ' ','
Ich würde das Array als Zeichenfolge wiedergeben, dann die Leerzeichen in Zeilenvorschübe umwandeln und dann
paste
alles in einer Zeile wie folgt verbinden:tr " " "\n" <<< "$FOO" | paste -sd , -
Ergebnisse:
a,b,c
Dies scheint mir das schnellste und sauberste zu sein!
quelle
$FOO
ist jedoch nur das erste Element des Arrays. Dies wird auch für Array-Elemente unterbrochen, die Leerzeichen enthalten.Bei der Wiederverwendung von @ spielt die Lösung keine Rolle, aber bei einer One-Anweisung wird die Substitution $ {: 1} vermieden und eine Zwischenvariable benötigt.
printf hat 'Die Formatzeichenfolge wird so oft wie nötig wiederverwendet, um die Argumente zu erfüllen.' in seinen Manpages, so dass die Verkettungen der Strings dokumentiert werden. Dann besteht der Trick darin, die LIST-Länge zu verwenden, um den letzten Sperator zu zerhacken, da beim Schneiden nur die Länge der LIST beibehalten wird, wenn die Felder zählen.
quelle
quelle
@Q
denfoo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"
'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
printf-Lösung, die Trennzeichen beliebiger Länge akzeptiert (basierend auf @ spielt keine Rolle, Antwort)
quelle
printf
Formatbezeichner (z. B.%s
unbeabsichtigt in$sep
wird Probleme verursachen.sep
kann mit saniert werden${sep//\%/%%}
. Ich mag deine Lösung besser als${bar#${sep}}
oder${bar%${sep}}
(alternativ). Dies ist hilfreich, wenn es in eine Funktion konvertiert wird, in der das Ergebnis in einer generischen Variablen wie__
und nichtecho
gespeichert wird.function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
quelle
HISTSIZE=0
?paste -sd,
nicht um die Verwendung von Geschichte.HISTSIZE=0
- probieren Sie es aus.Kürzere Version der Top-Antwort:
Verwendungszweck:
quelle
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; }
Dies funktioniert mit Verwendung:join_strings 'delim' "${array[@]}"
oder nichtjoin_strings 'delim' ${array[@]}
Kombinieren Sie das bisher Beste aller Welten mit der folgenden Idee.
Dieses kleine Meisterwerk ist
Beispiele:
quelle
join_ws ,
(ohne Argumente) gibt falsch aus,,
. 2.join_ws , -e
gibt fälschlicherweise nichts aus (das liegt daran, dass Sie falsch verwendenecho
anstattprintf
). Ich weiß eigentlich nicht, warum Sie die Verwendung vonecho
statt beworben habenprintf
:echo
ist notorisch kaputt undprintf
ist ein robustes eingebautes.Im Moment benutze ich:
Was funktioniert, aber (im allgemeinen Fall) schrecklich kaputt geht, wenn Array-Elemente ein Leerzeichen enthalten.
(Für Interessierte ist dies ein Wrapper-Skript um pep8.py )
quelle
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
. Der Operator$()
ist leistungsfähiger als Backtics (ermöglicht das Verschachteln von$()
und""
). Das Umschließen${TO_IGNORE[@]}
mit doppelten Anführungszeichen sollte ebenfalls helfen.Mein Versuch.
quelle
Verwenden Sie Perl für Trennzeichen mit mehreren Zeichen:
Oder in einer Zeile:
quelle
join
Name mit etwas Mist in Konflikt stehtOS X
.. ich würde es nennenconjoined
, oder vielleichtjackie_joyner_kersee
?Vielen Dank an @gniourf_gniourf für detaillierte Kommentare zu meiner Kombination der besten Welten bisher. Entschuldigung für den nicht gründlich entworfenen und getesteten Posting-Code. Hier ist ein besserer Versuch.
Diese Schönheit von der Empfängnis ist
Zusätzliche Beispiele:
quelle
Falls die Elemente, die Sie verbinden möchten, kein Array sind, sondern nur eine durch Leerzeichen getrennte Zeichenfolge, können Sie Folgendes tun:
Mein Anwendungsfall ist beispielsweise, dass einige Zeichenfolgen in meinem Shell-Skript übergeben werden und ich diese verwenden muss, um eine SQL-Abfrage auszuführen:
In my_script muss ich "SELECT * FROM table WHERE name IN ('aa', 'bb', 'cc', 'dd') ausführen. Dann ist der obige Befehl nützlich.
quelle
printf -v bar ...
die printf-Schleife verwenden, anstatt sie in einer Subshell ausführen und die Ausgabe erfassen zu müssen.Hier ist eine, die die meisten POSIX-kompatiblen Shells unterstützen:
quelle
local
).Die Verwendung der variablen Indirektion, um direkt auf ein Array zu verweisen, funktioniert ebenfalls. Benannte Referenzen können ebenfalls verwendet werden, wurden jedoch erst in 4.3 verfügbar.
Der Vorteil dieser Form einer Funktion besteht darin, dass Sie das Trennzeichen optional haben können (standardmäßig das erste Standardzeichen
IFS
, das ein Leerzeichen ist; machen Sie es möglicherweise zu einer leeren Zeichenfolge, wenn Sie möchten), und es vermeidet das zweimalige Erweitern von Werten (zuerst) wenn als Parameter übergeben, und zweitens als"$@"
innerhalb der Funktion).Bei dieser Lösung muss der Benutzer die Funktion auch nicht innerhalb einer Befehlssubstitution aufrufen, die eine Unterschale aufruft, um eine verknüpfte Version einer Zeichenfolge zu erhalten, die einer anderen Variablen zugewiesen ist.
Fühlen Sie sich frei, einen bequemeren Namen für die Funktion zu verwenden.
Dies funktioniert von 3.1 bis 5.0-alpha. Wie beobachtet, funktioniert die Variablen-Indirektion nicht nur mit Variablen, sondern auch mit anderen Parametern.
Arrays und Array-Elemente sind ebenfalls Parameter (Entitäten, die Werte speichern), und Verweise auf Arrays sind auch technisch Verweise auf Parameter. Und ähnlich wie die speziellen Parameter
@
,array[@]
macht auch eine gültige Referenz.Geänderte oder selektive Expansionsformen (wie die Teilstringerweiterung), die vom Parameter selbst abweichen, funktionieren nicht mehr.
Aktualisieren
In der Release-Version von Bash 5.0 wird die variable Indirektion bereits als indirekte Erweiterung bezeichnet und ihr Verhalten ist bereits im Handbuch explizit dokumentiert:
Beachten Sie, dass in der Dokumentation von
${parameter}
,parameter
als "Shell-Parameter wie beschrieben (in) PARAMETER oder Array-Referenz " bezeichnet wird. In der Dokumentation von Arrays wird erwähnt, dass "auf jedes Element eines Arrays mit verwiesen werden kann${name[subscript]}
". Dies macht__r[@]
eine Array-Referenz.Join durch Argumente Version
Siehe meinen Kommentar in Riccardo Gallis Antwort .
quelle
__
als Variablenname? Macht den Code wirklich unlesbar.Dieser Ansatz berücksichtigt Leerzeichen innerhalb der Werte, erfordert jedoch eine Schleife:
quelle
Wenn Sie das Array in einer Schleife erstellen, haben Sie folgende einfache Möglichkeit:
quelle
x=${"${arr[*]}"// /,}
Dies ist der kürzeste Weg, dies zu tun.
Beispiel,
quelle
bash: ${"${arr[*]}"// /,}: bad substitution
Vielleicht zu spät zur Party, aber das funktioniert bei mir:
quelle
Vielleicht fehlt mir etwas Offensichtliches, da ich ein Neuling in der ganzen Bash / Zsh-Sache bin, aber es sieht für mich so aus, als müssten Sie es überhaupt nicht verwenden
printf
. Es wird auch nicht wirklich hässlich, darauf zu verzichten.Zumindest hat es bei mir bisher ohne Probleme geklappt.
Zum Beispiel,
join \| *.sh
was, sagen wir, ich bin in meinem~
Verzeichnis, ausgegeben wirdutilities.sh|play.sh|foobar.sh
. Gut genug für mich.EDIT: Dies ist im Grunde die Antwort von Nil Geisweiller , aber verallgemeinert in eine Funktion.
quelle
Dies sorgt auch für das zusätzliche Komma am Ende. Ich bin kein Bash-Experte. Nur mein 2c, da dies elementarer und verständlicher ist
quelle
oder
quelle