在子 shell 中执行函数的变量范围

Scope of variables executing functions in a subshell

这是在子shell环境中执行命令时发生的情况:

  1. 该命令将运行复制到当前shell execution environment

  2. "Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes" (quote)

示例:

#!/bin/sh

export TOTO=123
FOO=abc

(mycmd)

在这种情况下,mycmd 将能够读取 TOTO 但不能读取 FOO,并且 mycmd 实现的对这两个变量值的每个更改在当前 shell.

中将不可见

但是当我们用函数做同样的事情时会发生什么?

示例:

#!/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

合理地,在子shell中执行的函数不能改变父shell中定义的变量的内容,另一方面它能够不加区别地读取所有变量即使它们没有被导出。

有人可以用更好的方式解释这种行为吗? 非常感谢指向一些参考的链接,因为我找不到任何相关信息。

您所看到的与功能无关。子外壳获取所有环境,甚至是未导出的变量。为了说明,让我们定义两个变量:

$ alpha=123
$ export beta=456

观察子 shell 可以访问两者:

$ (echo $alpha $beta)
123 456

但是,如果我们启动一个新进程,它只会看到导出的变量:

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

文档

man bash 将子 shell 记录如下:

(list)
list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes. The return status is the exit status of list.

如果我们去查看 "COMMAND EXECUTION ENVIRONMENT",我们发现它包括

shell parameters that are set by variable assignment or with set or inherited from the shell's parent in the environment.

换句话说,它包括变量无论是否它们已经导出。

如果我们进一步阅读,我们会发现这与 "a simple command other than a builtin or shell function." 形成对比,此类命令仅接收导出的变量。

这是一个简短的回答,希望能解释这些差异。它有两个函数myfunmyfunlocal。在 myfunlocal 中,totofoo 都被声明为具有 totofoolocal 副本,在函数中更新时不会反映回来在脚本的主体中,虽然 myfun 没有进行 local 重新声明,因此,对 totofoo 的更改反映在主体中。但是,如果 (myfun) 在子 shell 中是 运行,则对 totofoo 的更改不会反映在脚本的 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