Область действия локальных переменных в функциях оболочки

28

После прочтения 24.2. Локальные переменные , я думал, что объявление переменной varс ключевым словом localозначало, что varзначение было доступно только внутри блока кода, ограниченного фигурными скобками функции.

Однако после запуска следующего примера, я обнаружил, что varтакже может быть доступна, и считываться из функций , вызываемых этого блоком кода - то есть , даже если varдекларируются localк outerFunc, innerFuncвсе еще в состоянии прочитать и изменить его значение.

Run It Online

#!/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}]"

Выход:

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: Это ошибка в моей оболочке (bash 4.3.42, Ubuntu 16.04, 64bit) или это ожидаемое поведение?

РЕДАКТИРОВАТЬ: Решено. Как отмечает @MarkPlotnick, это действительно ожидаемое поведение.

maddouri
источник
Это ожидаемое поведение
fpmurphy
2
Я единственный, кто считает странным, что в последней строке вывода значение varпусто? varустановлен глобально innerFunc, так почему же он не придерживается до конца скрипта?
Гарольд Фишер

Ответы:

22

Переменные оболочки имеют динамическую область видимости . Если переменная объявлена ​​как локальная для функции, эта область остается до тех пор, пока функция не вернется.

Есть два исключения:

  1. в ksh93, если функция определена со стандартным function_name () { … }синтаксисом, то ее локальные переменные подчиняются динамической области видимости. Но если функция определена с синтаксисом ksh, function function_name { … }то ее локальная переменная подчиняется лексической / статической области видимости, поэтому они не видны в других функциях, вызываемых этим.

  2. zsh/privateautoloadable плагин в zshпозволяет с privateключевым словом / встроенным который может быть использовано , чтобы объявить переменный со статической сферой.

ash, bash, pdksh и производные, bosh имеют только динамическую область видимости.

Жиль "ТАК - перестань быть злым"
источник
Все ли переменные в оболочке имеют динамическую область видимости или это относится только к переменным, объявленным с помощью local?
Гарольд Фишер
@HaroldFischer Все переменные имеют динамическую область видимости. С объявлением typesetили declareили local, область действия пока функция не вернется. Без такой декларации область действия является глобальной.
Жиль "ТАК - прекрати быть злым"
6

Это не ошибка, вызов в контексте externalFunc использует эту локальную копию $ var. «Local» в externalFunc означает, что глобальное не изменилось. Если вы вызываете innerFunc вне externalFunc, то произойдет изменение в глобальном $ var, но не в локальный $ var externalFunc. Если бы вы добавили «local» к innerFunc, то $ var externalFunc не изменился бы - по сути, их было бы 3:

  • $ Глобальный :: вар
  • $ OuterFunc :: вар
  • $ InnerFunc :: вар

использовать формат пространства имен Perl, вроде.

afbach
источник
2

Вы можете использовать функцию для форсирования локальной области видимости:

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

Пример:

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"

Результат:

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]

Источник

Стивен Пенни
источник
2

В не был объявлен как местный , поэтому он доступен в видимом объеме (после того , как функция была вызвана). function innerFunc()var='new value'

И наоборот, в был объявлен как местный , поэтому он не доступен в глобальной области (даже если функция была вызвана).function outerFunc()local var='initial value'

Поскольку он innerFunc()был вызван как потомок outerFunc(), var находится в локальной области видимости outerFunc().

man 1 bash может помочь уточнить

местный [опция] [имя [= значение] ...]

Для каждого аргумента создается локальная переменная с именем name и присваиваемое значение. Опция может быть любой из опций, принимаемых объявить. Когда в функции используется local, это приводит к тому, что имя переменной имеет видимую область действия, ограниченную этой функцией и ее дочерними элементами. ...

Подразумеваемое поведение, которое ожидается в описании, может быть достигнуто путем объявления local var='new valueв function innerFunc().

Как уже говорили другие, это не ошибка в оболочке bash. Все работает как надо.

Джозеф Тингирис
источник
Ваше первое утверждение противоречит тому, что видит пользователь. Печать значения varв глобальном масштабе, после вызова innerFuncчерез outFunc, не печатает new value.
Кусалананда