如果源脚本在函数中运行,为什么 bash 变量不是全局变量?

and*_*sel 5 bash scope

通常从另一个脚本获取文件,我可以访问它的变量。

如果我从函数中获取脚本,则其变量不是全局变量,这似乎与联机帮助页相矛盾:

FUNCTION 函数的局部变量可以用 local 内置命令声明。通常,变量及其值在函数及其调用者之间共享。

source filename [arguments]在当前 shell 环境中从 filename 读取和执行命令

发生在我所有方便可用的版本中:3.2.57(1)-release (x86_64-apple-darwin17)、4.3.42(1)-release (x86_64-suse-linux-gnu) 和 4.3.48(1) -发布 (x86_64-pc-linux-gnu)

测试sourced.sh:

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL
declare -x FOO=bar
foo() { echo funfoo=$FOO $$ $SHLVL ; }
Run Code Online (Sandbox Code Playgroud)

测试top.sh:

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL

funcsource () { source ./test-sourced.sh ; }
echo ==== funcsource...
funcsource
echo foo=$FOO
foo

echo ==== source...
source ./test-sourced.sh
echo foo=$FOO
foo
Run Code Online (Sandbox Code Playgroud)

我看到了这个输出,但希望看到 funcsource 和 source 做同样的事情:

$ ./test-top.sh 
./test-top.sh 1234 2
==== funcsource...
./test-sourced.sh 1234 2
foo=
funfoo= 1234 2
==== source...
./test-sourced.sh 1234 2
foo=bar 1234 2
funfoo=bar
Run Code Online (Sandbox Code Playgroud)

它是相同的 PID 和相同的 shell 级别,所以它看起来像是故意的行为。这是一个错误,还是我错过了什么?


更新:在source命令 DOES 给出它们的值后立即回显 $FOO 并在函数中运行 'foo' ,所以它们得到了那么远,但由于某种原因保持在函数作用域的本地。这似乎仍然与手册相矛盾。

Ant*_*ica 5

这种行为的原因是因为您正在使用declare内置的shell。根据help declare

在函数中使用时,declare使 NAME 成为本地的,就像local 命令一样。该-g选项禁止这种行为。

更改test-sourced.sh为 usedeclare -g而不是declare -x(将变量导出到环境)——或常规的 shell 变量赋值——应该显示预期的行为(变量是全局的):

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL

# Simple shell variable assignment (global by default)
FOO=bar

# Use declare to globally assign a value to the shell variable
declare -g FOO=bar

foo() { echo funfoo=$FOO $$ $SHLVL ; }
Run Code Online (Sandbox Code Playgroud)

如果您希望从该 shell 会话启动的未来子进程可以访问该变量,则将变量导出到环境中只会增加实用程序。如果这是您想要的,您可以使用以下任一 Bash 构造来确保变量是全局的和导出的:

export FOO=bar
declare -x -g FOO=bar
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。我通常从不使用 `declare`,但我团队中的某个人对此非常信奉,我认为 `declare -x` 与 `export` 相同。 (2认同)