Función de reconocimiento de llamadas en bash

Función de reconocimiento de llamadas en bash

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 evalla 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 callerincorporado y analice su salida.

Todas estas soluciones parecen muy torpes, así que tengo dos preguntas:

  1. ¿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

?

  1. ¿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.mkshyashdashzsh

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 factualizaciones porque se llama desde .g$ag

Eso contrasta con las variables declaradas con typesetin funciones declaradas con la kshsintaxis ( function f { ...; }) in ksh93o variables declaradas con privatein zshdonde 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 zshequivalente 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_assignmentsfunció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.

información relacionada