Função sensível ao chamador no bash

2

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

Por enquanto, eu obtenho o primeiro retornando escrevendo para uma variável passada uma string para ser eval ed pelo chamador.

 write_local_var_assignments variable_name; eval "$variable_name"

Eu imagino que posso conseguir o último tendo o chamador passando "$FUNCNAME" ou fazendo o chamado chamar o caller construído e analisar sua saída.

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

  1. É possível que o candidato 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? Obtendo o resultado diretamente sem análise ou substituição de comando seria bom.
por PSkocik 10.06.2016 / 17:19

2 respostas

6

Em bash (e ksh88 , mksh , yash , dash , zsh ), o escopo da variável local é dinâmico.

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 f das g updates $a , porque é chamada de g .

Isso está em contraste com as variáveis declaradas com typeset em funções declaradas com a sintaxe ksh ( function f { ...; } ) em ksh93 ou variáveis declaradas com private em zsh onde você obteria um:

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
    
por 10.06.2016 / 17:41
2

Se você não se importar em usar uma substituição de comando, existe uma função que permitirá retornar strings auto-evalentes para seu chamador, permitindo que você chame uma função run_local_var_assignments de sua criação assim:

$(run_local_var_assignments)

A função é assim:

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

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

O double eval e% q escaping feitos pela função estão simplesmente lá porque são necessários para obter corretamente novas linhas e outros caracteres especiais para o chamador.

Sua função poderia 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, então coisas como espaços nos valores exigem aspas como acima.

Chamar essa 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 uma matriz de instruções, mas eu frequentemente acho o método array útil para construir dinamicamente um bloco de instruções, então imaginei que isso seria um pouco mais complicado.

    
por 20.06.2018 / 21:58

Tags