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.
#!/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
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:
-
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 kshfunction 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. -
o
zsh/private
plugin autoloadable emzsh
fornece umprivate
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
oudeclare
oulocal
, 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 chamarinnerFunc
aoutFunc
, não imprimirnew 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]
var
esteja vazio?var
é definido globalmente eminnerFunc
, então por que ' não dura até o fim do script?