O que é expansão indireta? O que $ {! Var *} significa?

87

Estou lendo o " Guia Bash para Iniciantes ". Diz:

Se o primeiro caractere de PARAMETERfor um ponto de exclamação, o Bash usará o valor da variável formado a partir do restante de PARAMETERcomo o nome da variável; essa variável é então expandida e esse valor é usado no resto da substituição, ao invés do valor em PARAMETERsi. Isso é conhecido como expansão indireta.

O exemplo dado é:

franky ~> echo ${!N*}
NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

Não entendo muito bem aqui:

o valor da variável formada a partir do resto de PARAMETER

Como PARAMETERé justo !N*, então

o resto de PARAMETER

é justo N*. Como isso pode formar uma variável? Bash pesquisou todos os comandos possíveis lá?

Athos
fonte

Respostas:

112

Se você ler a bashpágina do manual, basicamente confirma o que você declarou:

Se o primeiro caractere do parâmetro for um ponto de exclamação ( !), um nível de indireção variável é introduzido. O Bash usa o valor da variável formada a partir do restante do parâmetro como o nome da variável; essa variável é então expandida e esse valor é usado no resto da substituição, ao invés do valor do próprio parâmetro. Isso é conhecido como expansão indireta.

No entanto, a partir daí:

As exceções são as expansões de ${!prefix*}e ${!name[@]}descrito abaixo.

${!prefix*}Nomes que correspondem ao prefixo. Expande-se para os nomes de variáveis ​​cujos nomes começam com prefixo, separados pelo primeiro caractere da IFSvariável especial.

Em outras palavras, seu exemplo particular ${!N*}é uma exceção à regra que você citou. Ele faz , no entanto, o trabalho como anunciado nos casos esperados, tais como:

$ export xyzzy=plugh ; export plugh=cave

$ echo ${xyzzy}  # normal, xyzzy to plugh
plugh

$ echo ${!xyzzy} # indirection, xyzzy to plugh to cave
cave
paxdiablo
fonte
1
Obrigado pela resposta. Quanto mais leio "Guia Bash para Iniciantes", mais me pergunto se a autora entende o que escreve.
LRDPRDX
24

Parece haver uma exceção quando a "indireção" fornecida termina em a *, como acontece aqui. Nesse caso, ele fornece todos os nomes de variáveis ​​que começam com a parte que você especificou ( Naqui). O Bash pode fazer isso porque rastreia variáveis ​​e sabe quais existem.

A verdadeira indireção é esta:
digamos que eu tenha uma variável $VARIABLEdefinida como 42e outra variável $NAMEdefinida como VARIABLE. ${!NAME}vai me dar 42. Você usa o valor de uma variável para informar o nome de outra:

$ NAME="VARIABLE"
$ VARIABLE=42
$ echo ${!NAME}
42
Kevin
fonte
6
Uau, quem diria que era tão fácil obter a resposta para o sentido da vida, do universo e tudo mais!
KomodoDave
3

Sim, ele procura todas as expansões possíveis de variáveis ​​após!. Se você tivesse feito:

echo ${!NP*}

você obteria apenas NPX_PLUGIN_PATH.

Considere o seguinte exemplo:

:~> export myVar="hi"
:~> echo ${!my*}
    myVar
:~> export ${!my*}="bye"
:~> echo $myVar
    bye
tpg2114
fonte
outras variáveis ​​que correspondem ao meu * também seriam definidas como "tchau"?
Anthony
1
@Anthony eu tentei, e se ${!my*}expandir para myA, myB, myA é exportado com seu valor atual e myB é definido como "bye" e exportado. Não é muito útil.
GKFX
3

Você atingiu uma exceção no processamento indireto, onde se o último caractere for *, todas as variáveis ​​que têm o prefixo fornecido antes serão retornadas.

Ignacio Vazquez-Abrams
fonte
Tirando o *caso, é o mesmo que ${${VAR}}?
chronospoon
1
@chronospoon,, ${${VAR}}mais abreviadamente gravável como ${$VAR}, não é válido, pois $VARretorna uma string, que não pode seguir o $sinal; para usar uma string como nome de variável, você precisa introduzir um nível de indireção (conforme citado na própria pergunta original), ou seja , você pode usar ${!VAR}, que faz exatamente o que você esperaria (erroneamente, mas compreensivelmente) ${$VAR}faz.
Enlico