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


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.