Indirection em um array Bash

4

Existe a possibilidade de escrever o seguinte script sem o loop?

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

IPv4_all=() 

for var in ${!IPv4_@}
do
   IPv4_all+=(${!var})
done

printf "'%s'\n" "${IPv4_all[@]}"

Algo como:

IPv4_all=${!${!IPv4_@}}
    
por Ray 25.07.2014 / 12:07

4 respostas

5

Esse pode ser o código Bash mais feio que eu já escrevi, mas ...

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

names=(${!IPv4_@})
eval "IPv4_all=(${names[@]/#/$})"
printf "'%s'\n" "${IPv4_all[@]}"

Olhe Ma, sem loop!

${names[@]/#/$} prepends $ para o início de cada elemento da matriz, combinando uma string vazia ancorada no início de cada elemento. Isso fornece uma matriz de desreferências variáveis, que podemos expandir dentro de eval para obter as referências de variáveis dentro do inicializador de array. Estes precisam ser duas linhas separadas porque você não pode aplicar expansões de múltiplos parâmetros ao mesmo tempo.

A saída é:

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'

como esperado.

É possível substituir a linha por:

IPv4_all=($(eval "echo ${names[@]/#/$}"))

em vez de eval a atribuição da matriz. Não tenho certeza se isso é melhor ou não.

Se os valores das variáveis puderem conter espaços ou outros caracteres IFS, você poderá alterar o eval:

eval "IPv4_all=($(printf '"$%s" ' "${names[@]}"))"

Isso duplamente aspas todas as desreferências de variáveis.

    
por 25.07.2014 / 12:38
1

Você não precisa da variável IPv4_all :

eval printf "\'%s\'\\n" $(printf "$%s\n" ${!IPv4_@})

Saída:

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'
    
por 25.07.2014 / 12:56
1

Minha participação competitiva no código basico mais feio e mais complicado; -):

eval 'declare(){ v=${2%%=*};[[ $v = IPv4_* ]]&&IPv4_all+=("${!v}");};'"$(declare -p)"
unset -f declare
    
por 25.07.2014 / 12:52
1

Isso funciona para mim, eu acho, mas vou aceitar que posso estar perdendo algum ponto fundamental:

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

IPv4_all=( $(set | sed '/IPv4_.*[=)]/!d;s///') )

printf "'%s'\n" "${IPv4_all[@]}"

OUTPUT

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'

Isso é melhor:

eval IPv4_all=( "$(set |
    grep -E '^IPv4_[_[:alnum:]]*=([^(]|$)' |
    sed 's/\([^=]*\).*/${+"$"} /')"
)

grep só recebe linhas seguras que correspondem à sua var. sed os envolve em tokens de expansão de parâmetro, de forma que eles avaliem para nada se não forem nomes de variáveis de shell atuais.

    
por 25.07.2014 / 12:37

Tags