¿Qué es la expansión indirecta? ¿Qué significa ${!var*}?

Resuelto athos asked hace 13 años • 6 respuestas

Estoy leyendo la " Guía Bash para principiantes ". Dice:

Si el primer carácter de PARAMETERes un signo de exclamación, Bash usa el valor de la variable formada a partir del resto de PARAMETERcomo nombre de la variable; luego, esta variable se expande y ese valor se usa en el resto de la sustitución, en lugar del valor en PARAMETERsí. Esto se conoce como expansión indirecta.

El ejemplo dado es:

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

No entiendo muy bien aquí:

el valor de la variable formada a partir del resto dePARAMETER

Como PARAMETERes justo !N*, entonces

el resto dePARAMETER

es solo N*. ¿Cómo podría esto formar una variable? ¿Bash buscó todos los comandos posibles allí?

athos avatar Dec 15 '11 12:12 athos
Aceptado

Si lees la bashpágina de manual, básicamente confirma lo que has dicho:

Si el primer carácter del parámetro es un signo de exclamación ( !), se introduce un nivel de dirección indirecta variable. Bash usa el valor de la variable formada a partir del resto del parámetro como nombre de la variable; Luego, esta variable se expande y ese valor se usa en el resto de la sustitución, en lugar del valor del parámetro en sí. Esto se conoce como expansión indirecta.

Sin embargo, siga leyendo desde allí:

Las excepciones a esto son las expansiones de ${!prefix*}y ${!name[@]}que se describen a continuación.

${!prefix*}Nombres que coinciden con el prefijo. Se expande a los nombres de variables cuyos nombres comienzan con prefijo, separados por el primer carácter de la IFSvariable especial.

En otras palabras, su ejemplo particular ${!N*}es una excepción a la regla que citó. Sin embargo , funciona como se anuncia en los casos esperados, como por ejemplo :

$ export xyzzy=plugh ; export plugh=cave

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

$ echo ${!xyzzy} # indirection, xyzzy to plugh to cave
cave
paxdiablo avatar Dec 15 '2011 05:12 paxdiablo

Parece haber una excepción cuando la "indirección" dada termina en a *, como ocurre aquí. En este caso, proporciona todos los nombres de variables que comienzan con la parte que especificó ( Naquí). Bash puede hacer eso porque rastrea las variables y sabe cuáles existen.

La verdadera dirección indirecta es esta:
digamos que tengo una variable $VARIABLEconfigurada en 42y tengo otra variable $NAMEconfigurada en VARIABLE. ${!NAME}me dará 42. Usas el valor de una variable para decirte el nombre de otra:

$ NAME="VARIABLE"
$ VARIABLE=42
$ echo ${!NAME}
42
Kevin avatar Dec 15 '2011 05:12 Kevin

bash dirección indirecta y/o nameref

Llegando tarde a esta pregunta, y debido a que ninguna otra respuesta habla sobre nameref ...

Usando sintaxis ${!var} de indirección :

~$ someVariable='Some content'
~$ var=someVariable

~$ echo $var
someVariable

~$ echo ${!var}
Some content

Usando la sintaxis namref ( declare -n)

Al usar nameref, no solo puede mostrar el contenido de la variable, sino que también puede completar la variable y obtener o establecer atributos.

~$ someVariable='Some content'
~$ declare -n var=someVariable
~$ echo $var
Some content
~$ echo ${var@A}
someVariable='Some content'

Esta sintaxis es útil para funciones:

function showVarDetail() {
    local -n var=$1
    printf 'Variable \47\44%s\47 is %d len, has [%s] flags and contain: %q\n' \
        "$1" "${#var}" "${var@a}" "$var"
}

(Nota: esta función es solo una muestra. ¡Esto no expandirá correctamente las matrices y las matrices asociativas !)

Entonces

~$ someVar='Hello world!'
~$ showVarDetail someVar
Variable '$someVar' is 12 len, has [] flags and contain: Hello\ world\!

~$ declare -r PI=3.14159265358979323844
~$ showVarDetail PI
Variable '$PI' is 22 len, has [r] flags and contain: 3.14159265358979323844

~$ declare -ir answerOfUltimateQestionOfLiveUniverseAndEverything=42
~$ showVarDetail answerOfUltimateQestionOfLiveUniverseAndEverything
Variable '$answerOfUltimateQestionOfLiveUniverseAndEverything' is 2 len, has [ir
] flags and contain: 42

Completar valores de variables usando nameref

¡Esto podría funcionar en ambos sentidos!

Aquí hay una pequeña función de muestra para ejecutar con dos nombres de variables como argumentos. La primera variable debe contener una cadena y la segunda variable se completará con el primer carácter del contenido de la primera variable, luego el contenido de la primera variable se desplazará en 1 carácter:

shift1char <variable string source> <variable target>
shift1char () { 
    local -n srcStr=$1 tgtVar=$2;
    tgtVar=${srcStr::1} srcStr=${srcStr:1}
}

Entonces

~$ someVar='Hello world!'

~$ shift1char someVar someChar

~$ showVarDetail someVar
Variable '$someVar' is 11 len, has [] flags and contain: ello\ world\!

~$ showVarDetail someChar
Variable '$someChar' is 1 len, has [] flags and contain: H

Con algunas pequeñas modificaciones:

showVarDetail() { 
    local _nam
    for _nam in "$@"; do
        local -n _var=$_nam
        printf \
             'Variable \47\44%s\47 is %d len, has [%s] flags and contain: %q\n' \
             "${_nam}" "${#_var}" "${_var@a}" "$_var"
    done
}
move1char() {
    local -n srcStr=$1 tgtVar=$2
    [[ -z $srcStr ]] && return 1
    tgtVar+=${srcStr::1} srcStr=${srcStr:1}
}
someVar='Hello world!' target=''
while move1char someVar target;do
    showVarDetail someVar target
done

debe producir:

Variable '$someVar' is 11 len, has [] flags and contain: ello\ world\!
Variable '$target' is 1 len, has [] flags and contain: H
Variable '$someVar' is 10 len, has [] flags and contain: llo\ world\!
Variable '$target' is 2 len, has [] flags and contain: He
Variable '$someVar' is 9 len, has [] flags and contain: lo\ world\!
Variable '$target' is 3 len, has [] flags and contain: Hel
Variable '$someVar' is 8 len, has [] flags and contain: o\ world\!
Variable '$target' is 4 len, has [] flags and contain: Hell
Variable '$someVar' is 7 len, has [] flags and contain: \ world\!
Variable '$target' is 5 len, has [] flags and contain: Hello
Variable '$someVar' is 6 len, has [] flags and contain: world\!
Variable '$target' is 6 len, has [] flags and contain: Hello\ 
Variable '$someVar' is 5 len, has [] flags and contain: orld\!
Variable '$target' is 7 len, has [] flags and contain: Hello\ w
Variable '$someVar' is 4 len, has [] flags and contain: rld\!
Variable '$target' is 8 len, has [] flags and contain: Hello\ wo
Variable '$someVar' is 3 len, has [] flags and contain: ld\!
Variable '$target' is 9 len, has [] flags and contain: Hello\ wor
Variable '$someVar' is 2 len, has [] flags and contain: d\!
Variable '$target' is 10 len, has [] flags and contain: Hello\ worl
Variable '$someVar' is 1 len, has [] flags and contain: \!
Variable '$target' is 11 len, has [] flags and contain: Hello\ world
Variable '$someVar' is 0 len, has [] flags and contain: ''
Variable '$target' is 12 len, has [] flags and contain: Hello\ world\!
F. Hauri - Give Up GitHub avatar Nov 11 '2021 07:11 F. Hauri - Give Up GitHub

Sí, busca todas las posibles expansiones de variables después del !. Si hubieras hecho:

echo ${!NP*}

obtendrías solo NPX_PLUGIN_PATH.

Considere el siguiente ejemplo:

:~> export myVar="hi"
:~> echo ${!my*}
    myVar
:~> export ${!my*}="bye"
:~> echo $myVar
    bye
tpg2114 avatar Dec 15 '2011 05:12 tpg2114

Ha encontrado una excepción en el procesamiento de indirección, donde si el último carácter es *, se devolverán todas las variables que tengan el prefijo dado antes.

Ignacio Vazquez-Abrams avatar Dec 15 '2011 05:12 Ignacio Vazquez-Abrams