Wenn ich ein Array wie dieses in Bash habe:
FOO=( a b c )
Wie füge ich die Elemente mit Kommas zusammen? Zum Beispiel, a,b,c
erzeugen.
Umschreiblösung von Pascal Pilz als Funktion in 100% pure 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 Sie printf verwenden, um Trennzeichen mit mehreren Zeichen zu unterstützen, indem Sie die Idee von @gniourf_gniourf verwenden
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
Noch eine andere Lösung:
#!/bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
bar=$(printf ",%s" "${foo[@]}")
bar=${bar:1}
echo $bar
Edit: Gleiches aber für mehrstelliges Trennzeichen für variable Länge:
#!/bin/bash
separator=")|(" # e.g. constructing regex, pray it does not contain %s
foo=('foo bar' 'foo baz' 'bar baz')
regex="$( printf "${separator}%s" "${foo[@]}" )"
regex="${regex:${#separator}}" # remove leading separator
echo "${regex}"
# Prints: foo bar)|(foo baz)|(bar baz
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
Vielleicht, z.B.
SAVE_IFS="$IFS"
IFS=","
FOOJOIN="${FOO[*]}"
IFS="$SAVE_IFS"
echo "$FOOJOIN"
Überraschenderweise ist meine Lösung noch nicht gegeben :) Dies ist der einfachste Weg für mich. Es braucht keine Funktion:
IFS=, eval 'joined="${foo[*]}"'
Hinweis: Diese Lösung funktioniert im Nicht-POSIX-Modus gut. Im POSIX-Modus sind die Elemente immer noch ordnungsgemäß verbunden, IFS=,
wird jedoch dauerhaft.
Hier ist eine 100% reine Bash-Funktion, die die Aufgabe erfüllt:
join() {
# $1 is return variable name
# $2 is sep
# $3... are the elements to join
local retname=$1 sep=$2 ret=$3
shift 3 || shift $(($#))
printf -v "$retname" "%s" "$ret${@/#/$sep}"
}
Aussehen:
$ a=( one two "three three" four five )
$ join joineda " and " "${a[@]}"
$ echo "$joineda"
one and two and three three and four and five
$ join joinedb randomsep "only one element"
$ echo "$joinedb"
only one element
$ join joinedc randomsep
$ echo "$joinedc"
$ a=( $' stuff with\nnewlines\n' $'and trailing newlines\n\n' )
$ join joineda $'a sep with\nnewlines\n' "${a[@]}"
$ echo "$joineda"
stuff with
newlines
a sep with
newlines
and trailing newlines
$
Dadurch bleiben auch die nachfolgenden Zeilenumbrüche erhalten, und es ist keine Subshell erforderlich, um das Ergebnis der Funktion zu erhalten. Wenn Sie den printf -v
nicht mögen (warum sollten Sie ihn nicht mögen?) Und einen Variablennamen übergeben, können Sie natürlich eine globale Variable für den zurückgegebenen String verwenden:
join() {
# $1 is sep
# $2... are the elements to join
# return is in global variable join_ret
local sep=$1 IFS=
join_ret=$2
shift 2 || shift $(($#))
join_ret+="${*/#/$sep}"
}
Ich würde das Array als String wiederholen, dann die Leerzeichen in Zeilenvorschub umwandeln und dann paste
verwenden, um alles wie folgt in einer Zeile zu verbinden:
tr " " "\n" <<< "$FOO" | paste -sd , -
Ergebnisse:
a,b,c
Dies scheint mir das schnellste und sauberste zu sein!
Bei der Wiederverwendung von @ zählt die Lösung nicht, sondern mit einer one-Anweisung, indem die Substition $ {: 1} vermieden und eine Zwischenvariable benötigt wird.
echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )
printf hat 'Die Formatzeichenfolge wird so oft wiederverwendet, dass die Argumente erfüllt werden.' 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 hacken, da der Schnitt nur die Länge der LIST beibehält, wenn die Felder zählen.
Verwenden Sie keine externen Befehle:
$ FOO=( a b c ) # initialize the array
$ BAR=${FOO[@]} # create a space delimited string from array
$ BAZ=${BAR// /,} # use parameter expansion to substitute spaces with comma
$ echo $BAZ
a,b,c
Achtung, es wird davon ausgegangen, dass Elemente keine Leerzeichen enthalten.
s=$(IFS=, eval 'echo "${FOO[*]}"')
$ set a 'b c' d
$ history -p "[email protected]" | paste -sd,
a,b c,d
printf-Lösung, die Trennzeichen beliebiger Länge akzeptiert (basierend auf @ zählt die Antwort nicht)
#/!bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
sep=',' # can be of any length
bar=$(printf "${sep}%s" "${foo[@]}")
bar=${bar:${#sep}}
echo $bar
Dies unterscheidet sich nicht nur von vorhandenen Lösungen, aber es wird keine separate Funktion verwendet, IFS
wird in der übergeordneten Shell nicht geändert und steht in einer einzigen Zeile:
arr=(a b c)
printf '%s\n' "$(IFS=,; echo "${arr[*]}")"
ergebend
a,b,c
Einschränkung: Das Trennzeichen darf nicht länger als ein Zeichen sein.
Kürzere Version der Top-Antwort:
joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }
Verwendungszweck:
joinStrings "$myDelimiter" "${myArray[@]}"
Kombiniere das Beste aller Welten mit folgender Idee.
# join with separator
join_ws() { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }
Dieses kleine Meisterwerk ist
Beispiele:
$ join_ws , a b c
a,b,c
$ join_ws '' a b c
abc
$ join_ws $'\n' a b c
a
b
c
$ join_ws ' \/ ' A B C
A \/ B \/ C
Mein Versuch.
$ array=(one two "three four" five)
$ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")"
one SEP two SEP three four SEP five
Vielen Dank @gniourf_gniourf für ausführliche Kommentare zu meiner Kombination der besten Welten. Bitte entschuldigen Sie, dass der Buchungscode nicht sorgfältig erstellt und getestet wurde. Hier ist ein besserer Versuch.
# join with separator
join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }
Diese Schönheit von Konzeption ist
Zusätzliche Beispiele:
$ join_ws '' a b c
abc
$ join_ws ':' {1,7}{A..C}
1A:1B:1C:7A:7B:7C
$ join_ws -e -e
-e
$ join_ws $'\033[F' $'\n\n\n' 1. 2. 3. $'\n\n\n\n'
3.
2.
1.
$ join_ws $
$
Die Verwendung der variablen Indirektion zum direkten Verweisen auf ein Array funktioniert ebenfalls. Es können auch benannte Referenzen verwendet werden, die jedoch erst in 4.3 verfügbar waren.
Die Verwendung dieser Form einer Funktion hat den Vorteil, dass das Trennzeichen optional sein kann (standardmäßig wird das erste Zeichen der Standardvariable IFS
verwendet, das ein Leerzeichen ist; wenn Sie möchten, kann es eine leere Zeichenfolge sein). erst, wenn als Parameter übergeben, und zweitens als "[email protected]"
in der Funktion).
Diese Lösung erfordert auch nicht, dass der Benutzer die Funktion innerhalb einer Befehlssubstitution aufruft, die eine Subshell aufruft, um eine verbundene Version einer Zeichenfolge einer anderen Variablen zuzuweisen.
Was die Nachteile angeht: Sie müssten vorsichtig sein, wenn Sie einen korrekten Parameternamen übergeben, und __r
würde Ihnen __r[@]
übergeben. Das Verhalten der Variablen-Indirektion zur Erweiterung anderer Parameterformen wird ebenfalls nicht explizit dokumentiert.
function join_by_ref {
__=
local __r=$1[@] __s=${2-' '}
printf -v __ "%s${__s//\%/%%}" "${!__r}"
__=${__%${__s}}
}
array=(1 2 3 4)
join_by_ref array
echo "$__" # Prints '1 2 3 4'.
join_by_ref array '%s'
echo "$__" # Prints '1%s2%s3%s4'.
join_by_ref 'invalid*' '%s' # Bash 4.4 shows "invalid*[@]: bad substitution".
echo "$__" # Prints nothing but newline.
Dies funktioniert von 3.1 bis 5.0-alpha. Wie erwähnt funktioniert die Indirektion von Variablen nicht nur mit Variablen, sondern auch mit anderen Parametern.
Ein Parameter ist eine Entität, die Werte speichert. Es kann ein Name sein, eine Nummer oder eines der Sonderzeichen, die unter Special .__ aufgeführt sind. Parameter. Eine Variable ist ein Parameter, der mit einem Namen bezeichnet wird.
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 der spezielle Parameter @
macht array[@]
auch eine gültige Referenz.
Geänderte oder selektive Erweiterungsformen (wie die Erweiterung der Teilzeichenfolge), die vom Parameter selbst abweichen, funktionieren nicht mehr.
Im Moment verwende ich:
TO_IGNORE=(
E201 # Whitespace after '('
E301 # Expected N blank lines, found M
E303 # Too many blank lines (pep8 gets confused by comments)
)
ARGS="--ignore `echo ${TO_IGNORE[@]} | tr ' ' ','`"
Was funktioniert, wird (im allgemeinen Fall) jedoch fürchterlich brechen, wenn Arrayelemente ein Leerzeichen enthalten.
(Für Interessenten ist dies ein Wrapper-Skript um pep8.py )
Die meisten POSIX-kompatiblen Shells unterstützen Folgendes:
join_by() {
# Usage: join_by "||" a b c d
local arg arr=() sep="$1"
shift
for arg in "[email protected]"; do
if [ 0 -lt "${#arr[@]}" ]; then
arr+=("${sep}")
fi
arr+=("${arg}") || break
done
printf "%s" "${arr[@]}"
}
Falls die Elemente, die Sie verbinden möchten, kein Array sind, sondern nur eine durch Leerzeichen getrennte Zeichenfolge, können Sie Folgendes tun:
foo="aa bb cc dd"
bar=`for i in $foo; do printf ",'%s'" $i; done`
bar=${bar:1}
echo $bar
'aa','bb','cc','dd'
mein Anwendungsfall ist zum Beispiel, dass einige Zeichenfolgen in meinem Shell-Skript übergeben werden, und ich muss dies verwenden, um eine SQL-Abfrage auszuführen:
./my_script "aa bb cc dd"
In my_script muss ich "SELECT * FROM table WHERE name IN" ('aa', 'bb', 'cc', 'dd') ausführen. Dann ist der obige Befehl nützlich.
Verwenden Sie Perl für Trennzeichen mit mehreren Zeichen:
function join {
Perl -e '$s = shift @ARGV; print join($s, @ARGV);' "[email protected]";
}
join ', ' a b c # a, b, c
Oder in einer Zeile:
Perl -le 'print join(shift, @ARGV);' ', ' 1 2 3
1, 2, 3
Dieser Ansatz berücksichtigt Leerzeichen innerhalb der Werte, erfordert jedoch eine Schleife:
#!/bin/bash
FOO=( a b c )
BAR=""
for index in ${!FOO[*]}
do
BAR="$BAR,${FOO[$index]}"
done
echo ${BAR:1}
x=${"${arr[*]}"// /,}
Dies ist der kürzeste Weg.
Beispiel,
arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x # output: 1,2,3,4,5
Wenn Sie das Array in einer Schleife erstellen, ist dies ein einfacher Weg:
arr=()
for x in $(some_cmd); do
arr+=($x,)
done
arr[-1]=${arr[-1]%,}
echo ${arr[*]}