¿Qué es la expansión indirecta? ¿Qué significa ${!var*}?
Estoy leyendo la " Guía Bash para principiantes ". Dice:
Si el primer carácter de
PARAMETER
es un signo de exclamación, Bash usa el valor de la variable formada a partir del resto dePARAMETER
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 enPARAMETER
sí. 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 de
PARAMETER
Como PARAMETER
es justo !N*
, entonces
el resto de
PARAMETER
es solo N*
. ¿Cómo podría esto formar una variable? ¿Bash buscó todos los comandos posibles allí?
Si lees la bash
pá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 laIFS
variable 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
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ó ( N
aquí). 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 $VARIABLE
configurada en 42
y tengo otra variable $NAME
configurada 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
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\!
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
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.