Tengo una función (=llamada) que debería declarar y asignar efectivamente un par de variables en su llamador. También debería poder saber cuál es el nombre de la persona que llama.
Por ahora, logro lo primero volviendo a escribir en una variable pasada una cadena para que eval
la persona que llama la envíe.
write_local_var_assignments variable_name; eval "$variable_name"
Me imagino que puedo lograr esto último haciendo que la persona que llama pase "$FUNCNAME"
o haciendo que la persona que llama llame al caller
incorporado y analice su salida.
Todas estas soluciones parecen muy torpes, así que tengo dos preguntas:
- ¿Es posible que la persona que llama asigne variables locales en el contexto de la persona que llama, sin la cooperación de esta?
Es decir, ¿puedo comprimir:
write_local_var_assignments variable_name; eval "$variable_name"
en solo
run_local_var_assignments
?
- ¿Existe una mejor manera de obtener el nombre de la persona que llama a una función? Sería bueno obtener el resultado directamente sin análisis ni sustitución de comandos.
Respuesta1
En bash
(y ,,,, ksh88
) , el alcance de la variable local es dinámico.mksh
yash
dash
zsh
Este código:
f() { a=2; echo "f: $a"; }
g() { local a=1; f; echo "g: $a"; }
a=0
g
echo "global: $a"
produce esta salida:
f: 2
g: 2
global: 0
Esa es la variable de f
actualizaciones porque se llama desde .g
$a
g
Eso contrasta con las variables declaradas con typeset
in funciones declaradas con la ksh
sintaxis ( function f { ...; }
) in ksh93
o variables declaradas con private
in zsh
donde obtendrías:
f: 2
g: 1
global: 2
Entonces, en ese caso, no es necesario que hagas nada.
Para saber el nombre de la función que lo llama, en bash
, puede usar${FUNCNAME[1]}
.
El zsh
equivalente es $funcstack[2]
:
$ zsh -c 'f() { echo $funcstack[2]; }; g() { f; }; g'
g
$ bash -c 'f() { echo "${FUNCNAME[1]}"; }; g() { f; }; g'
g
Respuesta2
Si no le importa usar una sustitución de comando, hay una función que le permitirá devolver cadenas autoevaluadas a su interlocutor, permitiéndole llamar a una run_local_var_assignments
función de su creación de la siguiente manera:
$(run_local_var_assignments)
La función se ve así:
emit () {
local IFS=$'\n'
printf 'eval eval %q' "$*"
}
Emite una declaración de evaluación muy parecida a la eval "$variable_name"
anterior. Dado que es lo primero en la línea resultante, bash ejecuta la evaluación emitida como un comando.
La doble evaluación y el escape %q realizados por la función simplemente están ahí porque son necesarios para que las nuevas líneas y otros caracteres especiales lleguen correctamente a la persona que llama.
Entonces su función podría escribirse:
run_local_var_assignments () {
local assignments=()
assignments+=( 'local myvar1="my value1"' )
assignments+=( 'local myvar2="my value2"' )
emit "${assignments[@]}"
}
Las asignaciones están escritas como si fueran código fuente, por lo que cosas como espacios en los valores requieren comillas como se indica arriba.
Llamar a esta función en una sustitución de comando (como en la parte superior de esta respuesta) creará esas variables locales en el alcance de la persona que llama, con esos valores.
También podría usar una cadena normal o un documento heredoc en lugar de una matriz de declaraciones, pero con frecuencia encuentro útil el método de matriz para construir dinámicamente un bloque de declaraciones, así que pensé en mostrarlo aunque sea un poco más complicado.