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 eval
editada 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 caller
interno e analise sua saída.
Todas essas soluções parecem muito desajeitadas, então tenho duas perguntas:
- É 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
?
- 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.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"
produz esta saída:
f: 2
g: 2
global: 0
Essa é a variável de f
atualizações porque é chamada from .g
$a
g
Isso contrasta com as variáveis declaradas com typeset
funções declaradas com a ksh
sintaxe ( function f { ...; }
) in ksh93
ou variáveis declaradas com private
in zsh
onde 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 zsh
equivalente é $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_assignments
funçã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.