Escopo das variáveis locais em funções Shell

Depois de ler 24.2. Variáveis locais , pensei que declarar uma variável var com a palavra-chave local significava que var “s valor só estava acessível dentro do bloco de código delimitado pelas chaves de uma função.

No entanto, após executar o exemplo a seguir, descobri que var também pode ser acessado, lido e escrito a partir das funções invocadas por esse bloco de código – ou seja, mesmo que var seja declarado local para outerFunc, innerFunc ainda é capaz de lê-lo e alterar seu valor.

Execute on-line

#!/usr/bin/env bash function innerFunc() { var="new value" echo "innerFunc: [var:${var}]" } function outerFunc() { local var="initial value" echo "outerFunc: before innerFunc: [var:${var}]" innerFunc echo "outerFunc: after innerFunc: [var:${var}]" } echo "global: before outerFunc: [var:${var}]" outerFunc echo "global: after outerFunc: [var:${var}]" 

Resultado:

global: before outerFunc: [var:] # as expected, `var` is not accessible outside of `outerFunc` outerFunc: before innerFunc: [var:initial value] innerFunc: [var:new value] # `innerFunc` has access to `var` ?? outerFunc: after innerFunc: [var:new value] # the modification of `var` by `innerFunc` is visible to `outerFunc` ?? global: after outerFunc: [var:] 

Q : Isso é um bug no meu shell (bash 4.3.42, Ubuntu 16.04, 64 bits) ou é o comportamento esperado?

EDITAR: Resolvido. Conforme observado por @MarkPlotnick, este é realmente o comportamento esperado.

Comentários

  • É o comportamento esperado
  • Sou eu o único que acha ' estranho que na última linha de saída o valor de var esteja vazio? var é definido globalmente em innerFunc, então por que ' não dura até o fim do script?

Resposta

As variáveis do shell têm um escopo dinâmico . Se uma variável é declarada como local para uma função, esse escopo permanece até que a função retorne, incluindo durante chamadas para outras funções.

Existem duas exceções:

  1. em ksh93, se uma função for definida com a sintaxe function_name () { … } padrão, então suas variáveis locais obedecem ao escopo dinâmico. Mas se uma função é definida com a sintaxe ksh function function_name { … }, então sua variável local obedece ao escopo léxico / estático, portanto, não são visíveis em outras funções chamadas por este.

  2. o zsh/private plugin autoloadable em zsh fornece um private palavra-chave / embutida que pode ser usada para declarar uma variável com escopo estático.

ash, bash, pdksh e derivados, bosh só tem escopo dinâmico.

Comentários

  • Todas as variáveis no shell têm um escopo dinâmico ou isso se aplica apenas a variáveis declaradas com local?
  • @HaroldFischer Todas as variáveis têm escopo dinâmico. Com uma declaração typeset ou declare ou local, o escopo é até o retorno da função. Sem tal declaração, o escopo é global.
  • IMHO, If a variable is declared as local to a function, that scope remains until the function returns. não é suficiente para explicar o que é escopo dinâmico e escopo léxico. A descrição por si só também é aplicada ao escopo léxico.
  • @jinbeomhong Não, com escopo léxico, uma variável de uma função não é visível enquanto esta função chama outra função. Eu ' adicionei uma frase para declarar isso explicitamente.
  • Isso também afeta os builtins de chamada de função? Ou os builtins têm seu próprio escopo?

Resposta

Não é um bug, a chamada dentro do contexto de outerFunc usa aquela cópia local de $ var. O “local” em outerFunc significa que o global não foi alterado. Se você chamar innerFunc fora de outerFunc, haverá uma alteração no $ var global, mas não no local $ var de outerFunc. Se você adicionou “local” a innerFunc, então outerFunc “s $ var não” seria alterado – em essência, deve haver 3 deles:

  • $ global :: var
  • $ outerFunc :: var
  • $ innerFunc :: var

para usar o formato de namespace de Perl, mais ou menos.

Resposta

Em function innerFunc() o var="new value" não foi declarado como local , portanto, está disponível em escopo visível (uma vez a função foi chamada).

Por outro lado, em function outerFunc() o local var="initial value" foi declarado como local , portanto, não está disponível no escopo global (mesmo que a função tenha sido chamada).

Porque innerFunc() foi chamado como filho de outerFunc(), var está no escopo local de outerFunc().

man 1 bash pode ajudar a esclarecer

local [opção] [nome [= valor] …]

Para cada argumento, um local variável chamada nome é criada e valor atribuído. A opção pode ser qualquer uma das opções aceitas por declarar.Quando local é usado em uma função, faz com que o nome da variável tenha um escopo visível restrito a essa função e seus filhos. …

O comportamento implícito que “é esperado na descrição poderia ser alcançado declarando local var="new value em function innerFunc().

Como outros afirmaram, este não é um bug no shell bash. Tudo está funcionando como deveria.

Comentários

  • Sua primeira afirmação contradiz o que o usuário está vendo. Imprimir o valor de var no escopo global, depois de chamar innerFunc a outFunc, não imprimir new value.

Resposta

Você pode usar um função para forçar o escopo local:

sh_local() { eval "$(set)" command eval "\"\$@\"" } 

Exemplo:

x() { z="new value" printf "function x, z = [%s]\n" "$z" } y() { z="initial value" printf "function y before x, z = [%s]\n" "$z" sh_local x printf "function y after x, z = [%s]\n" "$z" } printf "global before y, z = [%s]\n" "$z" y printf "global after y, z = [%s]\n" "$z" 

Resultado:

global before y, z = [] function y before x, z = [initial value] function x, z = [new value] function y after x, z = [initial value] global after y, z = [initial value] 

Fonte

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *