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


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, это действительно ожидаемое поведение.


Это ожидаемое поведение
— 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, вроде.


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.
— Кусалананда
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.