Como unir elementos de uma matriz no Bash?

416

Se eu tiver uma matriz como esta no Bash:

FOO=( a b c )

Como uno os elementos com vírgulas? Por exemplo, produzindo a,b,c.

David Wolever
fonte

Respostas:

571

Solução de reescrita de Pascal Pilz como uma função no Bash 100% puro (sem comandos externos):

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

Por exemplo,

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

Como alternativa, podemos usar printf para suportar delimitadores de vários caracteres, usando a ideia de @gniourf_gniourf

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

Por exemplo,

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
Nicholas Sushkin
fonte
9
Use isso para separadores de vários caracteres: function join {perl -e '$ s = shift @ARGV; print join ($ s, @ARGV); ' "$ @"; } junte-se a ',' abc # a, b, c
Daniel Patru
4
@patrru mesmo assim para fazer essa festança pura?
CMCDragonkai
4
@puchu O que não funciona são os separadores de vários caracteres. Dizer "espaço não funciona" faz parecer que a união com um espaço não funciona. Faz.
Eric
6
Isso promove subcascas geradoras se o armazenamento da saída for variável. Use konsoleboxestilo :) function join { local IFS=$1; __="${*:2}"; }ou function join { IFS=$1 eval '__="${*:2}"'; }. Então use __depois. Sim, sou eu quem promove o uso __como uma variável de resultado;) (e uma variável de iteração comum ou variável temporária). Se o conceito chega a um site popular wiki Bash, eles me copiada :)
konsolebox
6
Não coloque a expansão $dno especificador de formato de printf. Você pensa que está seguro desde que escapou, %mas existem outras advertências: quando o delimitador contém uma barra invertida (por exemplo, \n) ou quando o delimitador começa com um hífen (e talvez outros em que eu não consigo pensar agora). É claro que você pode corrigi-las (substituir barras invertidas por barras duplas e usar printf -- "$d%s"), mas em algum momento você sentirá que está lutando contra o shell em vez de trabalhar com ele. É por isso que, na minha resposta abaixo, acrescentei o delimitador aos termos a serem unidos.
Gnourf_gniourf
206

Mais uma solução:

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

echo $bar

Editar: o mesmo, mas para o separador de comprimento variável de vários caracteres:

#!/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
não importa
fonte
7
+1. Que tal printf -v bar ",%s" "${foo[@]}". É um a forkmenos (na verdade clone). É ainda bifurcação lendo um arquivo: printf -v bar ",%s" $(<infile).
TrueY
14
Em vez de orar $separatornão contém %sou tal, você pode fazer o seu printfrobusto: printf "%s%s" "$separator" "${foo[@]}".
Musiphil 13/09/13
5
@musiphil Errado. From bash man: "O formato é reutilizado conforme necessário para consumir todos os argumentos. O uso de dois espaços reservados de formato como em printf "%s%s"usaria separador no conjunto de saída ONLY de primeira instância e, em seguida, simplesmente concatenaria o restante dos argumentos.
AnyDev
3
@AndrDevEK: Obrigado por pegar o erro. Em vez disso, eu sugeriria algo como printf "%s" "${foo[@]/#/$separator}".
Musiphil 22/09/14
2
@musiphil, Obrigado. Sim! Em seguida, printf se torna redundante e essa linha pode ser reduzida para IFS=; regex="${foo[*]/#/$separator}". Neste ponto, isso se torna essencialmente a resposta de gniourf_gniourf, que o IMO é mais limpo desde o início, ou seja, usando a função para limitar o escopo das alterações e variações temporárias do IFS.
AnyDev 22/09
145
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
Pascal Pilz
fonte
3
As aspas duplas externas e as aspas duplas ao redor dos dois pontos não são necessárias. Somente as aspas duplas internas são necessárias:bar=$( IFS=, ; echo "${foo[*]}" )
ceving 11/11/12
8
+1 para a solução mais compacta que não precisa de loops, que não precisa de comandos externos e que não impõe restrições adicionais ao conjunto de caracteres dos argumentos.
ceving 11/09/12
22
Eu gosto da solução, mas ele só funciona se IFS é um personagem
Jayen
8
Alguma idéia de por que isso não funciona se estiver sendo usado em @vez de *, como em $(IFS=, ; echo "${foo[@]}")? Eu posso ver que o *já preserva o espaço em branco nos elementos, mais uma vez não sei como, já que @geralmente é necessário para esse efeito.
haridsv
10
Encontrei a resposta para minha própria pergunta acima. A resposta é que o IFS é reconhecido apenas por *. Na página de manual do bash, procure por "Parâmetros especiais" e procure a explicação ao lado de *:
haridsv 16/04/14
66

Talvez, por exemplo,

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

echo "$FOOJOIN"
Martin Clayton
fonte
3
Se você fizer isso, ele acha que IFS- é a variável. Você precisa fazer echo "-${IFS}-"(os chavetas separam os traços do nome da variável).
Pausado até novo aviso.
1
Ainda obteve o mesmo resultado (eu só colocar os traços no para ilustrar o ponto ... echo $IFSfaz a mesma coisa.
David Wolever
41
Dito isto, isso ainda parece funcionar ... Então, como a maioria das coisas com Bash, vou fingir que entendo e continuar minha vida.
7119 David Wolever
2
Um "-" não é um caractere válido para um nome de variável; portanto, o shell faz a coisa certa quando você usa $ IFS-, não precisa de $ {IFS} - (bash, ksh, sh e zsh no linux e solaris também concordo).
Idelic 6/10/09
2
@ David A diferença entre seu eco e o de Dennis é que ele usou aspas duplas. O conteúdo do IFS é usado 'na entrada' como uma declaração de caracteres separadores de palavras - para que você sempre tenha uma linha vazia sem aspas.
27680 Martin Martine
30

Surpreendentemente, minha solução ainda não foi dada :) Essa é a maneira mais simples para mim. Não precisa de uma função:

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

Nota: Observou-se que esta solução funciona bem no modo não POSIX. No modo POSIX , os elementos ainda são unidos corretamente, mas IFS=,se tornam permanentes.

konsolebox
fonte
infelizmente funciona apenas para delimitadores de caractere único
maoizm
24

Aqui está uma função Bash 100% pura que faz o trabalho:

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

Veja:

$ 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


$

Isso preserva até as novas linhas finais e não precisa de um subshell para obter o resultado da função. Se você não gosta do printf -v(por que não gosta?) E passa um nome de variável, é claro que pode usar uma variável global para a string retornada:

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}"
}
gniourf_gniourf
fonte
1
Sua última solução é muito boa, mas pode ser melhorada criando join_retuma variável local e repetindo-a no final. Isso permite que o join () seja usado da maneira usual de script de shell, por exemplo $(join ":" one two three), e não requer uma variável global.
James Sneeringer 27/03
1
@JamesSneeringer Eu propositadamente usei esse design para evitar sub-conchas. No script de shell, diferentemente de muitos outros idiomas, as variáveis ​​globais usadas dessa maneira não são necessariamente uma coisa ruim; especialmente se eles estão aqui para ajudar a evitar sub-conchas. Além disso, $(...)apara novas linhas; portanto, se o último campo da matriz contiver novas linhas à direita, elas serão aparadas (consulte a demonstração em que não são aparadas com meu design).
27615 Gnourf_gniourf
Isso funciona com separadores de vários caracteres, o que me faz feliz ^ _ ^
spiffytech
Para abordar o "por que você não gostaria de printf -v?": No Bash, as variáveis ​​locais não são realmente locais de função, para que você possa fazer coisas assim. (Chame a função f1 com a variável local x, que por sua vez chama a função f2 que modifica x - que é declarada local no escopo de f1) Mas não é exatamente como as variáveis ​​locais devem funcionar. Se as variáveis ​​locais são realmente locais (ou se supõe que sejam, por exemplo, em um script que deve funcionar tanto no bash quanto no ksh), isso causa problemas com todo esse "retorno de um valor armazenando-o na variável com esse nome".
tetsujin 6/09/16
15

Isso não é muito diferente das soluções existentes, mas evita o uso de uma função separada, não modifica IFSno shell pai e é tudo em uma única linha:

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

resultando em

a,b,c

Limitação: o separador não pode ter mais de um caractere.

Benjamin W.
fonte
13

Usando nenhum comando externo:

$ 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

Aviso, assume que os elementos não têm espaços em branco.

Nil Geisweiller
fonte
4
Se você não quiser usar uma variável intermediária, que pode ser feito ainda mais curto:echo ${FOO[@]} | tr ' ' ','
jesjimher
2
Eu não entendo os votos negativos. É uma solução muito compacta e legível do que outras postadas aqui, e é claramente avisado de que não funciona quando há espaços.
jesjimher
12

Gostaria de ecoar a matriz como uma string, depois transformar os espaços em feeds de linha e, em seguida, usar pastepara juntar tudo em uma linha da seguinte maneira:

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

Resultados:

a,b,c

Este parece ser o mais rápido e limpo para mim!

Yanick Girouard
fonte
$FOOé apenas o primeiro elemento da matriz. Além disso, isso interrompe os elementos da matriz que contêm espaços.
Benjamin W.
9

Com a reutilização de @, não importa a solução, mas com uma declaração, evitando a subestação $ {: 1} e a necessidade de uma variável intermediária.

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

printf tem 'A string de formato é reutilizada quantas vezes for necessário para satisfazer os argumentos.' em suas páginas de manual, para que as concatenações das strings sejam documentadas. O truque é usar o comprimento LIST para cortar o último sperator, pois o corte reterá apenas o comprimento de LIST à medida que os campos forem contados.

Valise
fonte
7
s=$(IFS=, eval 'echo "${FOO[*]}"')
enguia ghEEz
fonte
8
Você deve elaborar sua resposta.
joce
O melhor. Obrigado!!
Peter pan gz
4
Eu gostaria de poder rebater esta resposta porque abre uma brecha na segurança e porque destrói espaços em elementos.
enguia ghEEz
1
@ bxm, de fato, parece preservar espaços e não permite escapar do contexto dos argumentos de eco. Eu percebi que a adição @Qpoderia escapar os valores unidas a partir interpretando mal quando eles têm um marceneiro neles: foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"saídas'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
enguia ghEEz
1
Evite soluções que façam uso de subcascas, a menos que seja necessário.
precisa saber é o seguinte
5

solução printf que aceita separadores de qualquer tamanho (com base em @ não importa resposta)

#/!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
Riccardo Galli
fonte
Isso produz saída com uma vírgula à esquerda.
precisa saber é o seguinte
A última barra = $ {bar: $ {# sep}} remove o separador. Eu apenas copio e colo em um shell bash e funciona. Qual shell você está usando?
Riccardo Galli
2
Qualquer printf especificador de formato (por exemplo, %sinvoluntariamente) $sepcausará problemas.
Peter.O
seppode ser higienizado com ${sep//\%/%%}. Gosto mais da sua solução ${bar#${sep}}ou ${bar%${sep}}(alternativa). Isso é bom se convertido em uma função que armazena o resultado em uma variável genérica como __, e não echoela.
precisa saber é o seguinte
function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
precisa saber é o seguinte
4
$ set a 'b c' d

$ history -p "$@" | paste -sd,
a,b c,d
Steven Penny
fonte
Isso deve estar no topo.
Eric Walker
6
Isso não deve estar no topo: e se HISTSIZE=0?
precisa saber é o seguinte
@ har-wradim, o truque paste -sd,não é sobre o uso da história.
Veda
@Veda Não, é sobre o uso da combinação, e não funcionaria se HISTSIZE=0- experimente.
precisa saber é o seguinte
4

Versão mais curta da resposta principal:

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

Uso:

joinStrings "$myDelimiter" "${myArray[@]}"
Camilo Martin
fonte
1
Uma versão mais longa, mas não há necessidade de fazer uma cópia de uma fatia dos argumentos para uma variável de matriz:join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
Rockallite
Ainda outra versão: join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; } Funciona com o uso: join_strings 'delim' "${array[@]}"ou sem join_strings 'delim' ${array[@]}
aspas
4

Combine o melhor de todos os mundos até agora com a ideia a seguir.

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

Esta pequena obra-prima é

  • 100% pure bash (expansão de parâmetro com o IFS temporariamente desabilitado, sem chamadas externas, sem impressão ...)
  • compacto, completo e sem falhas (funciona com limitadores de um e vários caracteres, funciona com limitadores que contêm espaços em branco, quebras de linha e outros caracteres especiais do shell, funciona com delimitador vazio)
  • eficiente (sem subcamação, sem cópia de matriz)
  • simples e estúpido e, até certo ponto, bonito e instrutivo também

Exemplos:

$ 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
hóspede
fonte
1
Não é tão legal: pelo menos 2 problemas: 1. join_ws ,(sem argumentos) gera incorretamente ,,. 2. join_ws , -egera nada de forma errada (é porque você está usando incorretamente em echovez de printf). Na verdade, não sei por que você anunciou o uso de, em echovez de printf: echoé notoriamente quebrado e printfé um componente robusto.
58520 Gnourf_gniourf #
1

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

O que funciona, mas (no caso geral) quebrará horrivelmente se os elementos da matriz tiverem um espaço neles.

(Para os interessados, este é um script de wrapper em torno de pep8.py )

David Wolever
fonte
de onde você obtém esses valores de matriz? se você está codificando assim, por que não apenas foo = "a, b, c".?
ghostdog74
Nesse caso, na verdade, estou codificando os valores, mas quero colocá-los em uma matriz para poder comentar sobre cada um individualmente. Atualizei a resposta para mostrar o que quero dizer.
7119 David Wolever
Supondo que você está realmente usando bash, isso pode funcionar melhor: ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')". O operador $()é mais poderoso que o backtics (permite o aninhamento de $()e ""). Quebra automática ${TO_IGNORE[@]}com aspas duplas também deve ajudar.
Kevinarpe
1

Minha tentativa.

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

Use perl para separadores de vários caracteres:

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

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

Ou em uma linha:

perl -le 'print join(shift, @ARGV);' ', ' 1 2 3
1, 2, 3
Daniel Patru
fonte
funciona para mim, embora o joinnome esteja em conflito com alguma porcaria OS X.. eu chamaria conjoined, ou talvez jackie_joyner_kersee?
11135 Alex
1

Obrigado @gniourf_gniourf pelos comentários detalhados sobre minha combinação dos melhores mundos até agora. Desculpe por postar código não totalmente projetado e testado. Aqui está uma tentativa melhor.

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

Essa beleza por concepção é

  • (ainda) 100% puro bash (obrigado por apontar explicitamente que printf também é embutido. Eu não sabia disso antes ...)
  • trabalha com delimitadores de vários caracteres
  • mais compacto e mais completo e, desta vez, cuidadosamente pensado e testado a longo prazo com substrings aleatórios de scripts de shell, entre outros, cobrindo o uso de caracteres especiais ou caracteres de controle do shell ou nenhum caractere no separador e / ou parâmetros e casos extremos , e casos de canto e outras queixas como nenhum argumento. Isso não garante que não haja mais erros, mas será um desafio um pouco mais difícil encontrar um. BTW, mesmo as respostas mais votadas atualmente e afins sofrem de coisas como esse -e bug ...

Exemplos adicionais:

$ 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 $ 
$
hóspede
fonte
1

Caso os elementos que você deseja juntar não sejam uma matriz apenas uma sequência separada por espaço, você pode fazer algo assim:

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

por exemplo, meu caso de uso é que algumas seqüências de caracteres são passadas no meu script de shell e preciso usá-lo para executar em uma consulta SQL:

./my_script "aa bb cc dd"

Em my_script, eu preciso fazer "SELECT * FROM tabela WHERE nome IN ('aa', 'bb', 'cc', 'dd'). Em seguida, o comando acima será útil.

Dexin Wang
fonte
Você pode usar em printf -v bar ...vez de precisar executar o loop printf em uma subcama e capturar a saída.
codeforester
todas as soluções extravagantes upvoted acima não trabalho, mas o seu um bruto fez por mim (Y)
ishandutta2007
1

Aqui está um que a maioria dos shells compatíveis com POSIX suporta:

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[@]}"
}
user541686
fonte
É um bom código Bash, mas o POSIX não possui matrizes (ou local).
Anders Kaseorg 16/01/19
@Anders: Sim, eu aprendi isso da maneira mais difícil muito recentemente :( Vou deixar isso por agora embora desde maioria dos shells compatíveis POSIX não parecem apoiar matrizes.
user541686
1

Usar a variável indireta para se referir diretamente a uma matriz também funciona. As referências nomeadas também podem ser usadas, mas apenas se tornaram disponíveis no 4.3.

A vantagem de usar essa forma de função é que você pode ter o separador opcional (o padrão é o primeiro caractere padrão IFS, que é um espaço; talvez seja uma string vazia, se você preferir), e evita expandir os valores duas vezes (primeiro quando passados ​​como parâmetros e depois como "$@"dentro da função).

Essa solução também não exige que o usuário chame a função dentro de uma substituição de comando - que convoca um subshell, para obter uma versão unida de uma string atribuída a outra variável.

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.

Sinta-se livre para usar um nome mais confortável para a função.

Isso funciona de 3.1 a 5.0-alfa. Como observado, a indireção de variáveis ​​não funciona apenas com variáveis, mas também com outros parâmetros.

Um parâmetro é uma entidade que armazena valores. Pode ser um nome, um número ou um dos caracteres especiais listados abaixo em Parâmetros especiais. Uma variável é um parâmetro indicado por um nome.

Matrizes e elementos de matriz também são parâmetros (entidades que armazenam valor) e referências a matrizes também são tecnicamente referências a parâmetros. E muito parecido com o parâmetro especial @,array[@] também faz uma referência válida.

Formas de expansão alteradas ou seletivas (como expansão de substring) que desviam a referência do próprio parâmetro não funcionam mais.

Atualizar

Na versão de lançamento do Bash 5.0, a indireção variável já é chamada de expansão indireta e seu comportamento já está explicitamente documentado no manual:

Se o primeiro caractere do parâmetro for um ponto de exclamação (!) E o parâmetro não for um nome, ele introduzirá um nível de indireção. O Bash usa o valor formado expandindo o restante do parâmetro como o novo parâmetro; isso é expandido e esse valor é usado no restante da expansão, em vez da expansão do parâmetro original. Isso é conhecido como expansão indireta.

Observando que na documentação de ${parameter}, parameteré referido como "um parâmetro de shell conforme descrito (em) PARÂMETROS ou uma referência de matriz ". E na documentação de matrizes, é mencionado que "Qualquer elemento de uma matriz pode ser referenciado usando ${name[subscript]}". Isso faz __r[@]uma referência de matriz.

Unir por versão de argumentos

Veja meu comentário na resposta de Riccardo Galli .

konsolebox
fonte
2
Existe um motivo específico para usar __como um nome de variável? Torna o código realmente ilegível.
PesaThe
@PesaThe É apenas uma preferência. Eu prefiro usar nomes genéricos para uma variável de retorno. Outros nomes não genéricos se atribuem a funções específicas e requerem memorização. Chamar várias funções que retornam valores em diferentes variáveis ​​pode tornar o código menos fácil de seguir. O uso de um nome genérico forçaria o scripter a transferir o valor da variável de retorno para a variável adequada para evitar conflitos e torna o código mais legível, pois fica explícito para onde vão os valores retornados. Porém, faço algumas exceções a essa regra.
konsolebox
0

Essa abordagem cuida dos espaços dentro dos valores, mas requer um loop:

#!/bin/bash

FOO=( a b c )
BAR=""

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

Se você criar a matriz em um loop, aqui está uma maneira simples:

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

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

Esta é a maneira mais curta de fazer isso.

Exemplo,

arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x  # output: 1,2,3,4,5
user31986
fonte
1
Isso não funciona corretamente para cadeias de caracteres com espaços: `t = (a" b c "d); eco $ {t [2]} (imprime "b c"); echo $ {"$ {t [*]}" // /,} (imprime a, b, c, d)
kounoupis 14/02/19
7
bash: ${"${arr[*]}"// /,}: bad substitution
Cameron Hudson
0

Talvez esteja atrasado para a festa, mas isso funciona para mim:

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

  echo "${output}"
}
TacB0sS
fonte
-1

Talvez esteja perdendo algo óbvio, já que sou um novato em toda a coisa do bash / zsh, mas parece-me que você não precisa usar printfnada. Nem fica realmente feio ficar sem.

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

Pelo menos, funcionou para mim até agora sem problemas.

Por exemplo, join \| *.shque, digamos que eu esteja no meu ~diretório, produzutilities.sh|play.sh|foobar.sh . Bom o suficiente para mim.

EDIT: Esta é basicamente a resposta de Nil Geisweiller , mas generalizada em uma função.

Jordânia
fonte
1
Eu não sou o menos favorável, mas manipular uma função global parece bastante maluco.
Tripleee
-2
liststr=""
for item in list
do
    liststr=$item,$liststr
done
LEN=`expr length $liststr`
LEN=`expr $LEN - 1`
liststr=${liststr:0:$LEN}

Isso também cuida da vírgula extra no final. Eu não sou especialista em bash. Apenas o meu 2c, já que isso é mais elementar e compreensível

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

ou

$ a=(1 "a b" 3)
$ b=$(IFS=, ; echo "${a[*]}")
$ echo $b
1,a b,3
Miau
fonte