Eu acho que você está perguntando duas coisas diferentes lá.
Existe uma maneira de fazer o bash imprimir essas informações sem o loop?
Sim, mas eles não são tão bons quanto usar o loop.
Existe uma maneira mais limpa de obter / imprimir apenas a parte key = value da saída?
Sim, o for
laço. Tem as vantagens de não exigir programas externos, é simples e facilita o controle exato do formato de saída sem surpresas.
Qualquer solução que tente manipular a saída de declare -p
( typeset -p
) deve lidar com a) a possibilidade de as próprias variáveis conterem parênteses ou colchetes, b) a citação que declare -p
deve ser adicionada para tornar sua entrada válida de saída para o shell.
Por exemplo, sua expansão b="${a##*(}"
consome alguns valores, se alguma chave / valor contiver um parêntese de abertura. Isso ocorre porque você usou ##
, o que remove o prefixo mais longo . O mesmo para c="${b%% )*}"
. Embora você possa, obviamente, corresponder ao padrão impresso de maneira declare
mais exata, você ainda teria dificuldade se não quisesse todas as citações.
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 for
loop, é mais fácil escolher o formato de saída como você gosta:
# 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 é simples alterar o formato de saída (remova os colchetes ao redor da chave, coloque todos os pares de chave / valor em uma única linha ...). Se você precisar citar algo diferente do próprio shell, ainda precisará fazer isso sozinho, mas pelo menos terá os dados brutos para trabalhar. (Se você tiver novas linhas nas chaves ou nos valores, provavelmente precisará de algumas aspas.)
Com um Bash atual (4.4, eu acho), você também pode usar em printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
vez de printf "%q=%q"
. Ele produz um formato citado um pouco melhor, mas é obviamente um pouco mais trabalhoso para se lembrar de escrever. (E cita a caixa de canto @
como chave de matriz, que %q
não cita.)
Se o loop for parecer muito cansativo para escrever, salve 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 basta 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
printf ...%q...
variante não é adequada para reinicialização no shell se a matriz tiver uma@
chave, pois% q não a cita ea=([@]=value)
for um erro de sintaxebash
."${x@Q}"
cita isso também, uma vez que cita todas as strings (e fica melhor). adicionou uma nota sobre o uso disso.zsh
com seus sinalizadores de expansão variáveis (que antecedem novamente o bash's por décadas e com o qual você pode escolher o estilo de citação: $ {(q) var}, $ {(qq) var} ...) para um design melhor. O bash tem o mesmo problema que o mksh, pois não cita a string vazia (não é um problema aqui, pois o bash não suporta chaves vazias). Além disso, ao usar estilos de citação diferentes de aspas simples (${var@Q}
recorre a$'...'
alguns valores), é importante que o código seja reinicializado no mesmo local.x=; echo "${x@Q}"
dá''
,unset x; echo "${x@Q}"
não dá nada.) O Bash@Q
parece preferir$'\n'
uma nova linha literal, o que pode ser bom em algumas situações (mas não sei dizer o que os outros preferem). Claro que ter uma escolha não seria ruim.$'...'
sintaxe é um problema potencial em coisas comoLC_ALL=zh_HK.big5hkscs bash -c 'a=$'\''\n\u3b1'\''; printf "%s\n" "${a@Q}"'
quais saídas$'\n<0xa3><0x5c>'
e0x5c
sozinha é barra invertida; portanto, você terá um problema se essa citação for interpretada em um código de idioma diferente.2 garfos
Talvez isto:
3 garfos
ou isto:
Sem garfo
para ser comparado com
Comparação dos tempos de execução
Como a última sintaxe não usa fork, eles podem ser mais rápidos:
Mas essa afirmação não permanece verdadeira se a matriz se tornar grande; se a redução de garfos é eficiente para processos pequenos, o uso de ferramentas dedicadas é mais eficiente para processos maiores.
Observação
Como ambas as soluções ( bifurcadas ) usam alinhamento , nenhuma delas funcionará se qualquer variável contiver uma nova linha . Nesse caso, a única maneira é um
for
loop.fonte
for
. O que é uma pena, realmente.pr
é mais curto ... Não tenho certeza se apr
sintaxe fica mais lenta, mesmo com grandes matrizes!${!array[@]}
e${array[@]}
primeiro para que isso funcionasse.paste
é mais longo que ofor
loop da pergunta escrita em uma linhafor i in "${!array[@]}"; do echo "$i=${array[$i]}" ; done
, mas requer duas subcascas e um programa externo. Como é que está mais limpo? A soluçãopr
também interrompe se houver muitos elementos, pois ele tenta paginar a saída. Você precisaria usar algo como o| pr -2t -l"${#array[@]}"
que está começando a ficar difícil de lembrar em comparação com o loop simples e, novamente, é mais longo que isso.bash
,cmd1 | cmd2
significa 2 garfos, mesmo que cmd1 ou cmd2 ou ambos estejam integrados.Se você está procurando um shell com melhor suporte a array associativo, tente
zsh
.Em
zsh
(onde as matrizes de associação foram adicionados em 1998, em comparação com 1993 para ksh93 e 2009 para bater),$var
ou${(v)var}
se expande para o (não vazios) valores de hash,${(k)var}
para as chaves (não vazios) (na mesma ordem), e${(kv)var}
para chaves e valores.Para preservar os valores vazios, como para matrizes, é necessário citar e usar o
@
sinalizador.Então, para imprimir as chaves e os valores, é apenas uma questão de
Apesar de explicar um hash possivelmente vazio, você deve fazer:
Observe também que o zsh usa uma sintaxe de definição de matriz muito mais sensível e útil do que
ksh93
a (copiada porbash
):O que facilita muito copiar ou mesclar matrizes associativas:
(você não pode copiar facilmente um hash sem um loop com
bash
e observe quebash
atualmente não suporta chaves vazias ou chaves / valores com bytes NUL).Consulte também os
zsh
recursos de zip da matriz que você normalmente precisará para trabalhar com matrizes associativas:fonte
Como o typeset faz o que você quer, por que não apenas editar sua saída?
dá
Onde
Detalhada, mas é muito fácil ver como a formatação funciona: basta executar o pipeline com progressivamente mais dos comandos sed e tr . Modifique-os para se adequar aos gostos bonitos da impressão.
fonte
sed
s etr
's não é muito mais simples do que umfor
loop comprintf
.tr
traduzir caractere por caractere não corresponde a cadeias de caracteres?tr "]=" " ="
muda "]" para um espaço e um=
para um=
, independentemente da posição. Então você provavelmente poderia combinar todos os trêstr
para um.Mais uma opção é listar todas as variáveis e grep para a que você deseja.
set | grep -e '^aa='
Eu uso isso para depuração. Duvido que seja muito eficiente, pois lista todas as variáveis.
Se você estivesse fazendo isso com frequência, poderia torná-lo uma função assim:
aap() { set | grep -e "^$1="; }
Infelizmente, quando verificamos o desempenho usando o tempo:
$ time aap aa aa=([0]="abc") . real 0m0.014s user 0m0.003s sys 0m0.006s
Portanto, se você estivesse fazendo isso com muita frequência, desejaria a versão NO FORKS do F.Hauri, porque é muito mais rápida.
fonte