2 garfos

2 garfos

Existe uma maneira de imprimir um array inteiro ([key]=value) sem fazer um loop em todos os elementos?

Suponha que eu criei um array com alguns elementos:

declare -A array
array=([a1]=1 [a2]=2 ... [b1]=bbb ... [f500]=abcdef)

Posso imprimir todo o array com

for i in "${!array[@]}"
do
echo "${i}=${array[$i]}"
done

No entanto, parece que o bash já sabe como obter todos os elementos do array de uma só vez - tanto chaves ${!array[@]}quanto valores ${array[@]}.

Existe uma maneira de fazer o bash imprimir essas informações sem o loop?

Editar:
typeset -p arrayfaz isso!
No entanto, não consigo remover o prefixo e o sufixo em uma única substituição:

a="$(typeset -p array)"
b="${a##*(}"
c="${b%% )*}"

Existe uma maneira mais limpa de obter/imprimir apenas a parte chave=valor da saída?

Responder1

Acho que você está perguntando duas coisas diferentes aí.

Existe uma maneira de fazer o bash imprimir essas informações sem o loop?

Sim, mas não são tão bons quanto usar o loop.

Existe uma maneira mais limpa de obter/imprimir apenas a parte chave=valor da saída?

Sim, o forcircuito. Tem a vantagem de não requer programas externos, é simples e torna bastante fácil controlar o formato exato de saída sem surpresas.


Qualquer solução que tente lidar com a saída de declare -p( typeset -p) tem que lidar com a) a possibilidade das próprias variáveis ​​conterem parênteses ou colchetes, b) as aspas que declare -pdevem ser adicionadas para tornar sua saída uma entrada válida para o shell.

Por exemplo, sua expansão b="${a##*(}"consome alguns dos valores, se alguma chave/valor contiver um parêntese de abertura. Isso ocorre porque você usou ##, que remove omais longoprefixo. O mesmo para c="${b%% )*}". Embora você possa, é claro, corresponder ao padrão impresso com declaremais exatidão, ainda seria difícil se não quisesse todas as citações que ele faz.

Isso não parece muito bom, a menos que você precise.

$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'

Com o forloop, é mais fácil escolher o formato de saída que desejar:

# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'

# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'

A partir daí também ésimplespara alterar o formato de saída (remova os colchetes ao redor da chave, coloque todos os pares chave/valor em uma única linha...). Se precisar de cotação para algo diferente do próprio shell, você ainda precisará fazer isso sozinho, mas pelo menos terá os dados brutos para trabalhar. (Se você tiver novas linhas nas chaves ou valores, provavelmente precisará de algumas citações.)

Com um Bash atual (4.4, eu acho), você também pode usar printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"em vez de printf "%q=%q". Ele produz um formato de citação um pouco melhor, mas é claro que é um pouco mais trabalhoso lembrar de escrever. (E cita o caso de canto @como chave de matriz, que %qnão cita.)

Se o loop for parecer muito cansativo para escrever, salve-o como uma função em algum lugar (sem citar aqui):

printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ;  }  

E então é só usar isso:

$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)

Também funciona com matrizes indexadas:

$ b=(abba acdc)
$ printarr b
0=abba
1=acdc

Responder2

declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'

2 garfos

Talvez isto:

printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1

printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb

printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2                              2
a1                              1
f50                             abcd
zz                              Hello World
b1                              bbb

3 garfos

ou isto:

paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

Sem garfo

ser comparado com

for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

Comparação de tempos de execução

Como a última sintaxe não usa fork, eles poderiam ser mais rápidos:

time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
      5      11      76
real    0m0.005s
user    0m0.000s
sys     0m0.000s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
      5       6      41
real    0m0.008s
user    0m0.000s
sys     0m0.000s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
      5       6      41
real    0m0.002s
user    0m0.000s
sys     0m0.001s

Mas esta afirmação não permanece verdadeira se o array se tornar grande; se a redução de garfos é eficiente para processos pequenos, o uso de ferramentas dedicadas é mais eficiente para processos maiores.

for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done


time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
  17581   35163  292941
real    0m0.150s
user    0m0.124s
sys     0m0.036s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
  17581   17582  169875
real    0m0.140s
user    0m0.000s
sys     0m0.004s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
  17581   17582  169875
real    0m0.312s
user    0m0.268s
sys     0m0.076s

Observação

Como ambos (bifurcado) soluções usamalinhamento, nenhum deles funcionará se alguma variável contiver umnova linha. Neste caso, a única maneira é um forloop.

Resposta mais robusta e detalhada no StackOverflow

Responder3

Bash 5.1 permite uma maneira muito direta de exibir matrizes associativas usando o Kvalor como em ${arr[@]@K}:

$ declare -A arr
$ arr=(k1 v1 k2 v2)
$ printf "%s\n" "${arr[@]@K}"
k1 "v1" k2 "v2" 

DeDocumento de descrição do Bash 5.1:

ah. Nova transformação de parâmetro `K' para exibir matrizes associativas como pares de valores-chave.

Está bem explicado noManual de referência do Bash → 3.5.3 Expansão de parâmetros do shell:

${parameter@operator}

K
Produz uma versão possivelmente citada do valor do parâmetro, exceto que imprime os valores de matrizes indexadas e associativas como uma sequência de pares de valores-chave entre aspas (consulte Matrizes).

Responder4

Desdeformatadofaz o que você quer, por que não apenas editar sua saída?

typeset -p array | sed s/^.*\(// | tr -d ")\'\""  | tr "[" "\n" | sed s/]=/' = '/

a2 = 2  
a1 = 1  
b1 = bbb 

Onde

array='([a2]="2" [a1]="1" [b1]="bbb" )'

Detalhado, mas é muito fácil ver como a formatação funciona: basta executar o pipeline com progressivamente mais dosedetrcomandos. Modifique-os para se adequarem aos gostos de impressão.

informação relacionada