Обсяг локальних змінних у оболонках


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) чи це очікувана поведінка?

EDIT: Вирішено. Як зазначає @MarkPlotnick, це дійсно очікувана поведінка.


Це очікувана поведінка
fpmurphy

2
Я єдиний, хто думає, що це дивно, що в останньому рядку виводу значення varпорожнього? varвстановлюється в усьому світі innerFunc, тож чому він не дотримується до кінця сценарію?
Гарольд Фішер

Відповіді:


22

Змінні оболонки мають динамічну область застосування . Якщо змінна оголошена локальною для функції, ця область залишається, поки функція не повернеться.

Є два винятки:

  1. у ksh93, якщо функція визначена зі стандартним function_name () { … }синтаксисом, то її локальні змінні підкоряються динамічному масштабуванню. Але якщо функція визначена з синтаксисом ksh, function function_name { … }то її локальна змінна підпорядковується лексичному / статичному масштабуванню, тому їх не видно в інших функціях, названих цим.

  2. zsh/privateautoloadable плагін в zshдозволяє з privateключовим словом / вбудованим який може бути використано , щоб оголосити змінний зі статичної сферою.

зола, баш, пдкш та похідні, бош мають лише динамічне масштабування.


Чи всі змінні в оболонці мають динамічну область застосування чи це стосується лише змінних, оголошених за допомогою local?
Гарольд Фішер,

@HaroldFischer Всі змінні мають динамічну сферу застосування. За допомогою typesetабо declareабо localдекларації, область дії є, поки функція не повернеться. Без такої декларації сфера застосування є глобальною.
Жил "ТАК - перестань бути злим"

6

Це не помилка, при виклику всередині контексту externalFunc використовується локальна копія $ var. "Місцеве" у externalFunc означає, що глобальне не змінюється. Якщо ви зателефонуєте InternalFunc за межами externalFunc, то відбудеться зміна глобального $ var, але не локального $ var зовнішньогоFunc. Якщо ви додали "local" до innerFunc, то $ var externalFunc не змінився б - по суті, їх було б 3:

  • $ global :: var
  • $ externalFunc :: var
  • $ innerFunc :: var

використовувати формат простору імен 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 може допомогти уточнити

локальний [варіант] [ім'я [= значення] ...]

Для кожного аргументу створюється локальна змінна назва з ім'ям і присвоюється значення. Опцією може бути будь-який з варіантів, прийнятих декларуванням. Якщо локальний використовується в межах функції, це призводить до того, що ім'я змінної має видимий обсяг, обмежений цією функцією та її дочірніми. ...

Має на увазі поведінку , яке очікується в описі може бути досягнуто шляхом оголошення local var='new valueв function innerFunc().

Як заявили інші, це не помилка в оболонці bash. Все функціонує як слід.


Ваше перше твердження суперечить тому, що бачить користувач. Друкуючи значення varв глобальному масштабі, після виклику innerFuncчерез outFuncнього не друкується new value.
Кусалаланда
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.