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

Вот что происходит, когда команда выполняется в среде подоболочки:

  1. Команда будет запущена в копии текущей среды выполнения оболочки.

  2. "Назначения переменных и встроенные команды, которые влияют на среду оболочки, не остаются в силе после завершения команды" (цитата)

Пример:

#!/bin/sh

export TOTO=123
FOO=abc

(mycmd)

В этом случае mycmd сможет читать TOTO, но не FOO, и каждое изменение значений этих двух переменных, реализованное mycmd, не будет видно в текущей оболочке.

Но что происходит, когда мы делаем то же самое с функцией?

Пример:

#!/bin/sh

export TOTO=123
FOO=abc

function (){
     echo $TOTO
     echo $FOO
     TOTO=${TOTO}456
     FOO=${FOO}def
}

(function)
echo $TOTO
echo $FOO

результат:

123
abc
123
abc

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

Может кто-нибудь объяснить это поведение лучше? Ссылки на некоторые ссылки будут очень признательны, так как я ничего не мог найти об этом.


person TTK    schedule 21.11.2015    source источник
comment
Спасибо. Я знаю об этом, но я хотел бы понять, почему функция, выполняемая в подоболочке, может читать все переменные, даже если они не экспортируются.   -  person TTK    schedule 21.11.2015
comment
Когда вы вызываете функцию в подоболочке, функция вместе с ее переменными выполняется в подоболочке, теперь я вижу ваш вопрос - то же правило - подоболочка имеет доступ к TOTO и FOO, которые являются областью действия функции, и если выполняется в этом подоболочка, не должны влиять на существующие значения TOTO и FOO, объявленные в основной части скрипта. Даже при отсутствии повторного объявления тот же результат будет применяться из-за того, что подоболочка наследует среду родителя.   -  person David C. Rankin    schedule 21.11.2015
comment
Итак, всякий раз, когда функция выполняется в подоболочке, она получает все переменные своей области? Есть ли способ избежать этого и выполнить его, как обычную команду?   -  person TTK    schedule 21.11.2015
comment
Да — вместе с копией окружения родителей (включая переменные), но функция может переобъявлять переменные, как это сделали вы. Если вы не переопределяете переменные в функции, то, когда НЕ запускается в подоболочке, она будет изменять переменные в основном теле, но при запуске в подоболочке ей запрещается изменять что-либо в родительской среде, включая переменные. (теперь он все еще может return целочисленное значение).   -  person David C. Rankin    schedule 21.11.2015
comment
Также обратите внимание, что подоболочки имеют свои собственные правила для того, какую среду они наследуют, независимо от того, выполняются ли они в новом процессе (что они обычно, но не обязаны делать): сравните echo $$ $BASHPID с (echo $$ $BASHPID) в bash 4 или более поздних версиях.   -  person chepner    schedule 21.11.2015


Ответы (2)


То, что вы наблюдаете, не имеет ничего общего с функциями. Вложенные оболочки получают всю среду, даже неэкспортированные переменные. Для иллюстрации давайте определим две переменные:

$ alpha=123
$ export beta=456

Обратите внимание, что подоболочка имеет доступ к обоим:

$ (echo $alpha $beta)
123 456

Однако если мы запустим новый процесс, он увидит только экспортированную переменную:

$ bash -c 'echo $alpha $beta'
456

Документация

man bash документирует подоболочки следующим образом:

(список)
список выполняется в среде подоболочки (см. СРЕДА ВЫПОЛНЕНИЯ КОМАНД ниже). Присвоения переменных и встроенные команды, влияющие на среду оболочки, не остаются в силе после завершения команды. Статус возврата - это статус выхода из списка.

Если мы посмотрим на «СРЕДУ ВЫПОЛНЕНИЯ КОМАНД», мы обнаружим, что она включает

параметры оболочки, которые задаются присваиванием переменной или с помощью set или унаследованы от родителя оболочки в среде.

Другими словами, он включает переменные, независимо от того, были ли они экспортированы.

Если мы прочитаем дальше, мы обнаружим, что это отличается от «простой команды, отличной от встроенной функции или функции оболочки». Такие команды получают только экспортированные переменные.

person John1024    schedule 21.11.2015
comment
Спасибо, это очень ясно. Я думал, что среда подоболочки не содержит всех переменных (как и любой нормальный не встроенный дочерний процесс). Тогда какой бы ни была (не встроенная) команда «mycmd» (первый пример), результат будет таким же, если он будет выполнен напрямую mycmd или в подоболочке (mycmd), я прав? - person TTK; 21.11.2015
comment
Да, mycmd независимо от того, выполняется ли он напрямую как mycmd или в подоболочке через (mycmd), увидит одну и ту же среду. - person John1024; 21.11.2015

Вот краткий ответ, который, надеюсь, объяснит различия. Он имеет две функции myfun и myfunlocal. В myfunlocal объявлено, что toto и foo имеют local копии toto и foo, которые при обновлении в функции не отражаются обратно в основной части скрипта, в то время как myfun не делает повторного объявления local и в результате изменяется на toto и foo отражаются в основном корпусе. Однако, если (myfun) запускается в подоболочке, изменения в toto и foo НЕ отражаются в main теле скрипта:

#!/bin/bash

function myfun {

    printf "\n               myfun - toto : %-6s  foo : %s\n" \
    "$toto" "$foo"

    toto=lizard
    foo=456

    printf "               myfun - toto : %-6s  foo : %s\n" \
    "$toto" "$foo"
}

function myfunlocal {

    printf "\n          myfunlocal - toto : %-6s  foo : %s\n" \
    "$toto" "$foo"

    local toto=lizard
    local foo=456

    printf "          myfunlocal - toto : %-6s  foo : %s\n" \
    "$toto" "$foo"
}

toto=dog
foo=123

printf "\n before myfunlocal   : toto : %-6s  foo : %s\n" "$toto" "$foo"

myfunlocal

printf "\n after myfunlocal    : toto : %-6s  foo : %s\n" "$toto" "$foo"

(myfun)

printf "\n after subshell myfun: toto : %-6s  foo : %s\n" "$toto" "$foo"

myfun

printf "\n after simple myfun  : toto : %-6s  foo : %s\n\n" "$toto" "$foo"

Вывод

$ bash functionsubshell.sh

 before myfunlocal   : toto : dog     foo : 123

          myfunlocal - toto : dog     foo : 123
          myfunlocal - toto : lizard  foo : 456

 after myfunlocal    : toto : dog     foo : 123

               myfun - toto : dog     foo : 123
               myfun - toto : lizard  foo : 456

 after subshell myfun: toto : dog     foo : 123

               myfun - toto : dog     foo : 123
               myfun - toto : lizard  foo : 456

 after simple myfun  : toto : lizard  foo : 456
person David C. Rankin    schedule 21.11.2015