Função de reconhecimento de chamada no bash

Função de reconhecimento de chamada no bash

Eu tenho uma função (=callee) que deve efetivamente declarar e atribuir algumas variáveis ​​em seu chamador. Ele também deve ser capaz de saber qual é o nome do chamador.

Por enquanto, consigo o primeiro retornando a gravação em uma variável passada de uma string a ser evaleditada pelo chamador.

 write_local_var_assignments variable_name; eval "$variable_name"

Imagino que posso conseguir o último fazendo com que o chamador passe "$FUNCNAME"ou fazendo com que o receptor chame o callerinterno e analise sua saída.

Todas essas soluções parecem muito desajeitadas, então tenho duas perguntas:

  1. É possível que o chamador atribua variáveis ​​locais ao contexto do chamador, sem a cooperação do chamador?

Ou seja, posso compactar:

 write_local_var_assignments variable_name; eval "$variable_name"

em apenas

 run_local_var_assignments

?

  1. Existe uma maneira melhor de obter o nome do chamador de uma função? Obter o resultado diretamente, sem análise ou substituição de comando, seria bom.

Responder1

Em bash(e ,,,,, ksh88) , o escopo da variável local é 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"

produz esta saída:

f: 2
g: 2
global: 0

Essa é a variável de fatualizações porque é chamada from .g$ag

Isso contrasta com as variáveis ​​declaradas com typesetfunções declaradas com a kshsintaxe ( function f { ...; }) in ksh93ou variáveis ​​declaradas com privatein zshonde você obteria:

f: 2
g: 1
global: 2

Então, nesse caso, você não precisa fazer nada.

Para saber o nome da função que está chamando você, em bash, você pode usar${FUNCNAME[1]}.

O zshequivalente é $funcstack[2]:

$ zsh -c 'f() { echo $funcstack[2]; }; g() { f; }; g'
g
$ bash -c 'f() { echo "${FUNCNAME[1]}"; }; g() { f; }; g'
g

Responder2

Se você não se importa em usar uma substituição de comando, existe uma função que permitirá que você retorne strings autoavaliadas ao seu chamador, permitindo que você chame uma run_local_var_assignmentsfunção de sua criação da seguinte forma:

$(run_local_var_assignments)

A função fica assim:

emit () {
  local IFS=$'\n'
  printf 'eval eval %q' "$*"
}

Ele emite uma instrução eval muito parecida com a eval "$variable_name"acima. Como é a primeira coisa na linha resultante, o bash executa o eval emitido como um comando.

O escape duplo eval e %q feito pela função estão simplesmente lá porque são necessários para enviar novas linhas e outros caracteres especiais para o chamador corretamente.

Sua função poderia então ser escrita:

run_local_var_assignments () {
  local assignments=()
  assignments+=( 'local myvar1="my value1"' )
  assignments+=( 'local myvar2="my value2"' )
  emit "${assignments[@]}"
}

As atribuições são escritas como se fossem código-fonte, portanto, coisas como espaços nos valores requerem aspas como acima.

Chamar esta função em uma substituição de comando (como no início desta resposta) criará essas variáveis ​​locais no escopo do chamador, com esses valores.

Você também pode usar uma string regular ou um heredoc em vez de um array de instruções, mas frequentemente acho o método array útil para construir dinamicamente um bloco de instruções, então resolvi mostrar isso, mesmo que seja um pouco mais complicado.

informação relacionada