Escopo de variáveis locais em funções do shell

18

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

No entanto, depois de executar o exemplo a seguir, descobri que var também pode ser acessado, lido e gravado a partir das funções invocadas por esse bloco de código - ou seja, mesmo que var seja declarado local to outerFunc , innerFunc ainda é capaz de ler e alterar seu valor.

Executar 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}]"

Saída:

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:]

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

EDITAR: Resolvido. Como observado por @MarkPlotnick, esse é realmente o comportamento esperado.

    
por 865719 11.05.2016 / 18:22

4 respostas

16

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.

Existe uma exceção: em ATT ksh, se uma função é definida com a sintaxe function_name () { … } padrã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, de modo que elas não são visíveis em outras funções chamadas por isso. Mas bash, mksh e zsh só têm escopo dinâmico.

    
por 12.05.2016 / 00:44
4

Não é um erro, a chamada dentro do contexto do outerFunc usa essa cópia local de $ var. O "local" no outerFunc significa que o global não é alterado. Se você chamar innerFunc fora de outerFunc, então haverá uma mudança para o $ var global, mas não o $ var local do outerFunc. Se você adicionou "local" ao innerFunc, então $ var do outerFunc não seria alterado - em essência, haveria 3 deles:

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

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

    
por 11.05.2016 / 19:16
1

Você pode usar uma 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]

Origem

    
por 06.04.2017 / 00:40
0

Em function innerFunc() o var='new value' não foi declarado como local , portanto está disponível no escopo visível (uma vez que a função tenha sido 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).

Como innerFunc() foi chamado como filho de outerFunc() , var está dentro do escopo local de outerFunc() .

man 1 bash pode ajudar a esclarecer

local [option] [name[=value] ...] For each argument, a local variable named name is created, and assigned value. The option can be any of the options accepted by declare. When local is used within a function, it causes the variable name to have a visible scope restricted to that function and its children. ...

O comportamento implícito esperado na descrição pode ser alcançado declarando local var='new value em function innerFunc() .

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

    
por 17.10.2018 / 19:57