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
.
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
konsolebox
estilo :)function join { local IFS=$1; __="${*:2}"; }
oufunction 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 :)$d
no especificador de formato deprintf
. 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 usarprintf -- "$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.Mais uma solução:
Editar: o mesmo, mas para o separador de comprimento variável de vários caracteres:
fonte
printf -v bar ",%s" "${foo[@]}"
. É um afork
menos (na verdadeclone
). É ainda bifurcação lendo um arquivo:printf -v bar ",%s" $(<infile)
.$separator
não contém%s
ou tal, você pode fazer o seuprintf
robusto:printf "%s%s" "$separator" "${foo[@]}"
.printf "%s%s"
usaria separador no conjunto de saída ONLY de primeira instância e, em seguida, simplesmente concatenaria o restante dos argumentos.printf "%s" "${foo[@]/#/$separator}"
.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.fonte
bar=$( IFS=, ; echo "${foo[*]}" )
@
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.*
. Na página de manual do bash, procure por "Parâmetros especiais" e procure a explicação ao lado de*
:Talvez, por exemplo,
fonte
echo "-${IFS}-"
(os chavetas separam os traços do nome da variável).echo $IFS
faz a mesma coisa.Surpreendentemente, minha solução ainda não foi dada :) Essa é a maneira mais simples para mim. Não precisa de uma função:
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.fonte
Aqui está uma função Bash 100% pura que faz o trabalho:
Veja:
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:fonte
join_ret
uma 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.$(...)
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).Isso não é muito diferente das soluções existentes, mas evita o uso de uma função separada, não modifica
IFS
no shell pai e é tudo em uma única linha:resultando em
Limitação: o separador não pode ter mais de um caractere.
fonte
Usando nenhum comando externo:
Aviso, assume que os elementos não têm espaços em branco.
fonte
echo ${FOO[@]} | tr ' ' ','
Gostaria de ecoar a matriz como uma string, depois transformar os espaços em feeds de linha e, em seguida, usar
paste
para 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!
fonte
$FOO
é apenas o primeiro elemento da matriz. Além disso, isso interrompe os elementos da matriz que contêm espaços.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.
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.
fonte
fonte
@Q
poderia 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 '
solução printf que aceita separadores de qualquer tamanho (com base em @ não importa resposta)
fonte
printf
especificador de formato (por exemplo,%s
involuntariamente)$sep
causará problemas.sep
pode 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ãoecho
ela.function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
fonte
HISTSIZE=0
?paste -sd,
não é sobre o uso da história.HISTSIZE=0
- experimente.Versão mais curta da resposta principal:
Uso:
fonte
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' "${@}"; }
Funciona com o uso:join_strings 'delim' "${array[@]}"
ou semjoin_strings 'delim' ${array[@]}
Combine o melhor de todos os mundos até agora com a ideia a seguir.
Esta pequena obra-prima é
Exemplos:
fonte
join_ws ,
(sem argumentos) gera incorretamente,,
. 2.join_ws , -e
gera nada de forma errada (é porque você está usando incorretamente emecho
vez deprintf
). Na verdade, não sei por que você anunciou o uso de, emecho
vez deprintf
:echo
é notoriamente quebrado eprintf
é um componente robusto.Agora eu estou usando:
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 )
fonte
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.Minha tentativa.
fonte
Use perl para separadores de vários caracteres:
Ou em uma linha:
fonte
join
nome esteja em conflito com alguma porcariaOS X
.. eu chamariaconjoined
, ou talvezjackie_joyner_kersee
?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.
Essa beleza por concepção é
Exemplos adicionais:
fonte
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:
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:
Em my_script, eu preciso fazer "SELECT * FROM tabela WHERE nome IN ('aa', 'bb', 'cc', 'dd'). Em seguida, o comando acima será útil.
fonte
printf -v bar ...
vez de precisar executar o loop printf em uma subcama e capturar a saída.Aqui está um que a maioria dos shells compatíveis com POSIX suporta:
fonte
local
).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.
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.
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:
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 .
fonte
__
como um nome de variável? Torna o código realmente ilegível.Essa abordagem cuida dos espaços dentro dos valores, mas requer um loop:
fonte
Se você criar a matriz em um loop, aqui está uma maneira simples:
fonte
x=${"${arr[*]}"// /,}
Esta é a maneira mais curta de fazer isso.
Exemplo,
fonte
bash: ${"${arr[*]}"// /,}: bad substitution
Talvez esteja atrasado para a festa, mas isso funciona para mim:
fonte
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
printf
nada. Nem fica realmente feio ficar sem.Pelo menos, funcionou para mim até agora sem problemas.
Por exemplo,
join \| *.sh
que, 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.
fonte
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
fonte
ou
fonte