Come posso unire elementi di un array in Bash?


417

Se ho un array come questo in Bash:

FOO=( a b c )

Come posso unire gli elementi con virgole? Ad esempio, producendo a,b,c.

Risposte:


571

Soluzione di riscrittura di Pascal Pilz in funzione di Bash puro al 100% (senza comandi esterni):

function join_by { local IFS="$1"; shift; echo "$*"; }

Per esempio,

join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c

In alternativa, possiamo usare printf per supportare delimitatori multi-carattere, usando l'idea di @gniourf_gniourf

function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }

Per esempio,

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

9
Usalo per i separatori multi-carattere: funzione join {perl -e '$ s = shift @ARGV; print join ($ s, @ARGV); ' "$ @"; } join ',' abc # a, b, c
Daniel Patru,

4
@dpatru comunque per fare quella pura festa?
CMCDragonkai,

4
@puchu Ciò che non funziona sono i separatori multi-carattere. Dire "lo spazio non funziona" fa sembrare che unirsi a uno spazio non funzioni. Lo fa.
Eric

6
Questo favorisce la generazione di sottotitoli se si memorizza l'output su variabile. Usa lo konsoleboxstile :) function join { local IFS=$1; __="${*:2}"; }o function join { IFS=$1 eval '__="${*:2}"'; }. Quindi utilizzare __dopo. Sì, sono quello che promuove l'uso di __come variabile di risultato;) (e una variabile di iterazione comune o variabile temporanea). Se il concept arriva su un popolare sito wiki di Bash, mi copiano :)
konsolebox

6
Non inserire l'espansione nell'identificatore $ddi formato di printf. Pensi di essere al sicuro da quando sei "sfuggito" al %ma ci sono altri avvertimenti: quando il delimitatore contiene una barra rovesciata (ad esempio, \n) o quando il delimitatore inizia con un trattino (e forse altri a cui non riesco a pensare ora). Naturalmente puoi risolverli (sostituisci le barre rovesciate con doppie barre rovesciate e usa printf -- "$d%s"), ma a un certo punto sentirai che stai combattendo contro la shell invece di lavorare con essa. Ecco perché, nella mia risposta di seguito, ho anteposto il delimitatore ai termini da unire.
gniourf_gniourf,

206

Ancora un'altra soluzione:

#!/bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
bar=$(printf ",%s" "${foo[@]}")
bar=${bar:1}

echo $bar

Modifica: uguale ma per separatore a lunghezza variabile multi-carattere:

#!/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

7
+1. Che dire printf -v bar ",%s" "${foo[@]}". È uno in forkmeno (in realtà clone). E 'anche forking lettura di un file: printf -v bar ",%s" $(<infile).
TrueY

14
Invece di pregare $separatornon contiene %so tali, è possibile rendere il printfrobusto: printf "%s%s" "$separator" "${foo[@]}".
musiphil,

5
@musiphil Wrong. Da bash man: "Il formato viene riutilizzato come necessario per consumare tutti gli argomenti. L'uso di due segnaposto di formato come in printf "%s%s"userebbe il separatore nella prima istanza SOLO set di output, e quindi semplicemente concatenerebbe il resto degli argomenti.
AnyDev

3
@AndrDevEK: Grazie per aver colto l'errore. Invece suggerirei qualcosa di simile printf "%s" "${foo[@]/#/$separator}".
musiphil,

2
@musiphil, grazie. Sì! Quindi printf diventa ridondante e quella riga può essere ridotta a IFS=; regex="${foo[*]/#/$separator}". A questo punto questa diventa essenzialmente la risposta di gniourf_gniourf che IMO è più pulita dall'inizio, ovvero usando la funzione per limitare l'ambito delle modifiche IFS e delle variazioni temporanee.
AnyDev,

145
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d

3
Le doppie virgolette esterne e le doppie virgolette attorno ai due punti non sono necessarie. Sono necessarie solo le doppie virgolette interne:bar=$( IFS=, ; echo "${foo[*]}" )
ceving

8
+1 per la soluzione più compatta che non ha bisogno di loop, che non ha bisogno di comandi esterni e che non impone ulteriori restrizioni al set di caratteri degli argomenti.
spasso l'

22
mi piace la soluzione, ma funziona solo se IFS è un personaggio
Jayen,

8
Qualche idea sul perché questo non funziona se si utilizza @invece di *, come in $(IFS=, ; echo "${foo[@]}")? Vedo che *conserva già lo spazio bianco negli elementi, ancora una volta non so come, poiché di @solito è necessario per questo motivo.
Haridsv,

10
Ho trovato la risposta per la mia domanda sopra. La risposta è che IFS è riconosciuto solo per *. Nella pagina man di bash, cerca "Parametri speciali" e cerca la spiegazione accanto a *:
haridsv

66

Forse, ad es.

SAVE_IFS="$IFS"
IFS=","
FOOJOIN="${FOO[*]}"
IFS="$SAVE_IFS"

echo "$FOOJOIN"

3
Se lo fai, pensa che IFS- sia la variabile. Devi fare echo "-${IFS}-"(le parentesi graffe separano i trattini dal nome della variabile).
In pausa fino a nuovo avviso.

1
Ho sempre ottenuto lo stesso risultato (ho appena inserito i trattini per illustrare il punto ... echo $IFSfa la stessa cosa.
David Wolever,

41
Detto questo, sembra che funzioni ancora ... Quindi, come la maggior parte delle cose con Bash, farò finta di capirlo e andare avanti con la mia vita.
David Wolever,

2
Un "-" non è un carattere valido per un nome di variabile, quindi la shell fa la cosa giusta quando usi $ IFS-, non hai bisogno di $ {IFS} - (bash, ksh, sh e zsh in Linux e Solaris anche d'accordo).
Idelic,

2
@David la differenza tra la tua eco e quella di Dennis è che ha usato la doppia citazione. Il contenuto di IFS viene utilizzato "in input" come una dichiarazione di caratteri separatori di parole, quindi otterrai sempre una riga vuota senza virgolette.
Martin Clayton,

30

Sorprendentemente la mia soluzione non è ancora stata data :) Questo è il modo più semplice per me. Non ha bisogno di una funzione:

IFS=, eval 'joined="${foo[*]}"'

Nota: questa soluzione ha funzionato bene in modalità non POSIX. In modalità POSIX , gli elementi sono ancora uniti correttamente, ma IFS=,diventano permanenti.


purtroppo funziona solo per i delimitatori a carattere singolo
maoizm,

24

Ecco una funzione Bash pura al 100% che fa il lavoro:

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}"
}

Guarda:

$ 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


$

Ciò preserva anche le nuove righe finali e non è necessaria una subshell per ottenere il risultato della funzione. Se non ti piace il printf -v(perché non ti piacerebbe?) E passando un nome di variabile, puoi ovviamente usare una variabile globale per la stringa restituita:

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}"
}

1
La tua ultima soluzione è molto buona, ma potrebbe essere resa più pulita creando join_retuna variabile locale e facendola eco alla fine. Ciò consente a join () di essere utilizzato nel solito modo di script di shell, ad esempio $(join ":" one two three), e non richiede una variabile globale.
James Sneeringer,

1
@JamesSneeringer Ho volutamente usato questo disegno per evitare le sottotitoli. Nello scripting della shell, a differenza di molti altri linguaggi, le variabili globali utilizzate in questo modo non sono necessariamente una cosa negativa; specialmente se sono qui per aiutare a evitare le conchiglie. Inoltre, $(...)ritaglia le nuove linee; quindi se l'ultimo campo dell'array contiene nuove righe finali, queste verrebbero tagliate (vedere la demo dove non sono state ritagliate con il mio disegno).
gniourf_gniourf

Funziona con separatori multi-carattere, il che mi rende felice ^ _ ^
spiffytech,

Per affrontare il "perché non ti piacerebbe printf -v?": In Bash, le variabili locali non sono veramente locali di funzione, quindi puoi fare cose del genere. (Chiama la funzione f1 con la variabile locale x, che a sua volta chiama la funzione f2 che modifica x - che è dichiarata locale nell'ambito di f1) Ma non è proprio come dovrebbero funzionare le variabili locali. Se le variabili locali sono realmente locali (o si presume che lo siano, ad esempio in uno script che deve funzionare sia su bash che su ksh), ciò causa problemi con l'intero schema "restituisci un valore memorizzandolo nella variabile con questo nome".
Tetsujin,

15

Questo non è troppo diverso dalle soluzioni esistenti, ma evita di utilizzare una funzione separata, non si modifica IFSnella shell padre ed è tutto in una sola riga:

arr=(a b c)
printf '%s\n' "$(IFS=,; printf '%s' "${arr[*]}")"

con il risultato di

a,b,c

Limitazione: il separatore non può essere più lungo di un personaggio.


13

Non usare comandi esterni:

$ 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

Attenzione, presuppone che gli elementi non abbiano spazi bianchi.


4
Se non vuoi usare una variabile intermedia, puoi farlo anche più breve:echo ${FOO[@]} | tr ' ' ','
jesjimher

2
Non capisco i voti negativi. È una soluzione molto compatta e leggibile rispetto ad altri pubblicati qui, ed è chiaramente avvertito che non funziona quando ci sono spazi.
jesjimher,

12

Vorrei fare eco all'array come stringa, quindi trasformare gli spazi in feed di riga e quindi utilizzare pasteper unire tutto in una riga in questo modo:

tr " " "\n" <<< "$FOO" | paste -sd , -

risultati:

a,b,c

Questo sembra essere il più veloce e pulito per me!


$FOOè solo il primo elemento dell'array, però. Inoltre, ciò si interrompe per gli elementi dell'array contenenti spazi.
Benjamin W.

9

Con il riutilizzo della soluzione @ non importa ', ma con una sola istruzione evitando la sottostizione $ {: 1} e la necessità di una variabile intermedia.

echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )

printf ha "La stringa di formato viene riutilizzata tutte le volte che è necessario per soddisfare gli argomenti". nelle sue pagine man, in modo che le concatenazioni delle stringhe siano documentate. Quindi il trucco consiste nell'utilizzare la lunghezza dell'ELENCO per tagliare l'ultimo speratore, poiché il taglio manterrà solo la lunghezza dell'ELENCO mentre i campi contano.


7
s=$(IFS=, eval 'echo "${FOO[*]}"')

8
Dovresti arricchire la tua risposta.
giovedì

Il migliore. Grazie!!
Peter Pan Gz,

4
Vorrei poter sottovalutare questa risposta perché apre una falla di sicurezza e perché distruggerà gli spazi negli elementi.
Eel GhEEz,

1
@bxm, in effetti, sembra preservare gli spazi e non consente di uscire dal contesto degli argomenti dell'eco. Ho pensato che l'aggiunta @Qpotesse sfuggire ai valori uniti dall'interpretazione errata quando foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
contenevano

1
Evitare soluzioni che fanno uso di subshells se non necessario.
konsolebox,

5

soluzione printf che accetta separatori di qualsiasi lunghezza (basato sulla risposta @ non importa)

#/!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

Questo produce output con una virgola iniziale.
Mark Renouf,

L'ultima barra = $ {bar: $ {# sep}} rimuove il separatore. Ho appena copiato e incollato in una shell bash e funziona. Che shell stai usando?
Riccardo Galli,

2
Qualsiasi printf di formato . (Ad es %sinvolontariamente nel $sepcauserà problemi.
Peter.O

seppuò essere disinfettato con ${sep//\%/%%}. Mi piace la tua soluzione meglio di ${bar#${sep}}o ${bar%${sep}}(alternativa). Questo è utile se convertito in una funzione che memorizza il risultato in una variabile generica come __, e non in echoessa.
konsolebox,

function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
konsolebox,

4
$ set a 'b c' d

$ history -p "$@" | paste -sd,
a,b c,d

Questo dovrebbe essere al top.
Eric Walker,

6
Questo non dovrebbe essere al top: e se HISTSIZE=0?
Har-Wradim,

@ har-wradim, il trucco paste -sd,non riguarda l'uso della storia.
Veda,

@Veda No, riguarda l'uso della combinazione e non funzionerebbe se HISTSIZE=0- provalo.
har-wradim,

4

Versione più breve della risposta principale:

joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }

Uso:

joinStrings "$myDelimiter" "${myArray[@]}"

1
Una versione più lunga, ma non è necessario eseguire una copia di una porzione degli argomenti in una variabile array:join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
Rockallite,

Ancora un'altra versione: join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; } funziona con l'utilizzo: join_strings 'delim' "${array[@]}"o non quotato:join_strings 'delim' ${array[@]}
Cometsong,

4

Combina il meglio di tutti i mondi finora con la seguente idea.

# join with separator
join_ws()  { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }

Questo piccolo capolavoro è

  • 100% puro bash (espansione dei parametri con IFS temporaneamente non impostato, nessuna chiamata esterna, nessuna stampa ...)
  • compatto, completo e impeccabile (funziona con limitatori a uno o più caratteri, funziona con limitatori contenenti spazi bianchi, interruzioni di riga e altri caratteri speciali della shell, funziona con delimitatore vuoto)
  • efficiente (nessuna subshell, nessuna copia dell'array)
  • semplice e stupido e, in una certa misura, anche bello e istruttivo

Esempi:

$ 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

1
Non è carino: almeno 2 problemi: 1. join_ws ,(senza argomenti) genera erroneamente ,,. 2. join_ws , -eerroneamente non produce nulla (questo perché stai usando erroneamente echoinvece di printf). In realtà non so perché tu abbia pubblicizzato l'uso echoinvece di printf: echoè notoriamente rotto, ed printfè un solido built-in.
gniourf_gniourf

1

In questo momento sto usando:

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 ' ' ','`"

Il che funziona, ma (nel caso generale) si romperà orribilmente se gli elementi dell'array hanno uno spazio al loro interno.

(Per gli interessati, questo è uno script wrapper attorno a pep8.py )


da dove prendi quei valori di array? se lo stai codificando in questo modo, perché non semplicemente foo = "a, b, c".?
ghostdog74,

In questo caso in realtà sto codificando i valori, ma voglio metterli in un array in modo da poter commentare ciascuno individualmente. Ho aggiornato la risposta per mostrarti cosa intendo.
David Wolever,

Supponendo che in realtà si sta utilizzando bash, questo potrebbe funzionare meglio: ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')". L'operatore $()è più potente dei backtic (consente l'annidamento di $()e ""). Anche avvolgere ${TO_IGNORE[@]}con virgolette doppie dovrebbe aiutare.
kevinarpe,

1

Il mio tentativo

$ array=(one two "three four" five)
$ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")"
one SEP two SEP three four SEP five

1

Usa perl per i separatori multi-carattere:

function join {
   perl -e '$s = shift @ARGV; print join($s, @ARGV);' "$@"; 
}

join ', ' a b c # a, b, c

O in una riga:

perl -le 'print join(shift, @ARGV);' ', ' 1 2 3
1, 2, 3

funziona per me, anche se il joinnome è in conflitto con qualche schifezza su OS X.. lo chiamerei conjoined, o forse jackie_joyner_kersee?
Alex Grey,

1

Grazie @gniourf_gniourf per commenti dettagliati sulla mia combinazione dei migliori mondi finora. Ci scusiamo per la pubblicazione di codice non completamente progettato e testato. Ecco un tentativo migliore.

# join with separator
join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }

Questa bellezza per concezione è

  • (ancora) 100% puro bash (grazie per aver esplicitamente sottolineato che printf è anche incorporato. Non ne ero a conoscenza prima ...)
  • funziona con delimitatori multi-carattere
  • più compatto e più completo e questa volta attentamente pensato e sottoposto a stress test a lungo termine con sottostringhe casuali da script di shell tra gli altri, che coprono l'uso di caratteri speciali di shell o caratteri di controllo o nessun carattere sia in separatore e / o parametri, sia in casi limite e casi angolari e altri cavilli come nessun argomento. Ciò non garantisce che non ci siano più bug, ma sarà una sfida un po 'più difficile trovarne uno. A proposito, anche le risposte attualmente più votate e relative soffrono di cose come quel bug ...

Esempi aggiuntivi:

$ 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 $ 
$

1

Nel caso in cui gli elementi che si desidera unire non siano un array, ma solo una stringa separata da spazi, è possibile fare qualcosa del genere:

foo="aa bb cc dd"
bar=`for i in $foo; do printf ",'%s'" $i; done`
bar=${bar:1}
echo $bar
    'aa','bb','cc','dd'

ad esempio, il mio caso d'uso è che alcune stringhe sono passate nel mio script shell e devo usarlo per eseguire una query SQL:

./my_script "aa bb cc dd"

In my_script, devo fare "SELEZIONA * DA tabella DOVE nome IN ('aa', 'bb', 'cc', 'dd'). Quindi il comando sopra sarà utile.


È possibile utilizzare printf -v bar ...invece di eseguire il ciclo printf in una subshell e acquisire l'output.
codeforester,

tutte le fantasiose soluzioni di cui sopra non hanno funzionato, ma la tua grezza ha funzionato per me (Y)
ishandutta2007,

1

Eccone uno supportato dalla maggior parte delle shell compatibili con POSIX:

join_by() {
    # Usage:  join_by "||" a b c d
    local arg arr=() sep="$1"
    shift
    for arg in "$@"; do
        if [ 0 -lt "${#arr[@]}" ]; then
            arr+=("${sep}")
        fi
        arr+=("${arg}") || break
    done
    printf "%s" "${arr[@]}"
}

Va bene il codice Bash, ma POSIX non ha affatto array (o local).
Anders Kaseorg,

@Anders: Sì, l'ho imparato nel modo più recente :( Per ora lo lascerò in
sospeso,

1

Funziona anche usando indiretta variabile per fare riferimento direttamente ad un array. I riferimenti nominati possono anche essere usati, ma sono diventati disponibili solo in 4.3.

Il vantaggio di utilizzare questa forma di una funzione è che puoi avere il separatore opzionale (il valore predefinito è il primo carattere di default IFS, che è uno spazio; forse, se lo desideri, rendilo una stringa vuota), ed evita di espandere i valori due volte (prima quando passato come parametri e secondo come "$@"all'interno della funzione).

Inoltre, questa soluzione non richiede all'utente di chiamare la funzione all'interno di una sostituzione di comando, che convoca una subshell, per ottenere una versione unita di una stringa assegnata a un'altra variabile.

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.

Sentiti libero di usare un nome più comodo per la funzione.

Funziona da 3.1 a 5.0-alfa. Come osservato, l'indirizzamento variabile non funziona solo con le variabili ma anche con altri parametri.

Un parametro è un'entità che memorizza valori. Può essere un nome, un numero o uno dei caratteri speciali elencati di seguito in Parametri speciali. Una variabile è un parametro indicato da un nome.

Anche le matrici e gli elementi della matrice sono parametri (entità che memorizzano il valore) e i riferimenti alle matrici sono anche tecnicamente riferimenti a parametri. E proprio come il parametro speciale @, array[@]fa anche un riferimento valido.

Le forme di espansione alterate o selettive (come l'espansione della sottostringa) che deviano il riferimento dal parametro stesso non funzionano più.

Aggiornare

Nella versione di rilascio di Bash 5.0, l'indirizzamento variabile è già chiamato espansione indiretta e il suo comportamento è già esplicitamente documentato nel manuale:

Se il primo carattere del parametro è un punto esclamativo (!) E il parametro non è un nameref, introduce un livello di riferimento indiretto. Bash utilizza il valore formato espandendo il resto del parametro come nuovo parametro; questo viene quindi espanso e quel valore viene utilizzato nel resto dell'espansione, piuttosto che nell'espansione del parametro originale. Questo è noto come espansione indiretta.

Tenendo presente che nella documentazione di ${parameter}, parameterviene indicato come "un parametro di shell come descritto (in) PARAMETRI o un riferimento di matrice ". E nella documentazione degli array, si dice che "Qualsiasi elemento di un array può essere referenziato usando ${name[subscript]}". Questo fa __r[@]un riferimento di matrice.

Partecipa per argomenti versione

Vedi il mio commento nella risposta di Riccardo Galli .


2
C'è un motivo specifico da utilizzare __come nome di variabile? Rende il codice davvero illeggibile.
Pesa

@PesaThe È solo una preferenza. Preferisco usare nomi generici per una variabile di ritorno. Altri nomi non generici si attribuiscono a funzioni specifiche e richiede la memorizzazione. Chiamare più funzioni che restituiscono valori su variabili diverse può rendere il codice meno facile da seguire. L'uso di un nome generico costringerebbe lo scripter a trasferire il valore dalla variabile return alla variabile corretta per evitare conflitti, e renderebbe il codice più leggibile poiché diventa esplicito dove vanno i valori restituiti. Faccio alcune eccezioni a quella regola però.
konsolebox,

0

Questo approccio si occupa degli spazi all'interno dei valori, ma richiede un ciclo:

#!/bin/bash

FOO=( a b c )
BAR=""

for index in ${!FOO[*]}
do
    BAR="$BAR,${FOO[$index]}"
done
echo ${BAR:1}

0

Se compili l'array in un ciclo, ecco un modo semplice:

arr=()
for x in $(some_cmd); do
   arr+=($x,)
done
arr[-1]=${arr[-1]%,}
echo ${arr[*]}

0

x=${"${arr[*]}"// /,}

Questo è il modo più breve per farlo.

Esempio,

arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x  # output: 1,2,3,4,5

1
Questo non funziona correttamente per la stringa con spazi: `t = (a" b c "d); echo $ {t [2]} (stampa "b c"); echo $ {"$ {t [*]}" // /,} (stampa a, b, c, d)
kounoupis

7
bash: ${"${arr[*]}"// /,}: bad substitution
Cameron Hudson il

0

Forse in ritardo per la festa, ma questo funziona per me:

function joinArray() {
  local delimiter="${1}"
  local output="${2}"
  for param in ${@:3}; do
    output="${output}${delimiter}${param}"
  done

  echo "${output}"
}

-1

Forse mi manca qualcosa di ovvio, dal momento che sono un principiante dell'intera cosa bash / zsh, ma mi sembra che non sia necessario utilizzare printfaffatto. Né diventa davvero brutto farne a meno.

join() {
  separator=$1
  arr=$*
  arr=${arr:2} # throw away separator and following space
  arr=${arr// /$separator}
}

Almeno, ha funzionato per me finora senza problemi.

Ad esempio, join \| *.shche, diciamo, sono nella mia ~directory, output utilities.sh|play.sh|foobar.sh. Abbastanza buono per me

EDIT: Questa è sostanzialmente la risposta di Nil Geisweiller , ma generalizzata in una funzione.


1
Non sono il downvoter, ma manipolare un globale in una funzione sembra piuttosto strano.
triplo il

-2
liststr=""
for item in list
do
    liststr=$item,$liststr
done
LEN=`expr length $liststr`
LEN=`expr $LEN - 1`
liststr=${liststr:0:$LEN}

Questo si occupa anche della virgola extra alla fine. Non sono un esperto di bash. Solo il mio 2c, poiché questo è più elementare e comprensibile


-2
awk -v sep=. 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"

o

$ a=(1 "a b" 3)
$ b=$(IFS=, ; echo "${a[*]}")
$ echo $b
1,a b,3
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.