为什么 Bash 的“source”命令在从函数中调用时表现不同?

Tim*_*ske 10 bash source shell-builtin

鉴于以下来源的barBash 脚本......

echo :$#:"$@":
Run Code Online (Sandbox Code Playgroud)

...以及以下可执行的fooBash 脚本:

echo -n source bar:
source bar
echo -n source bar foo:
source bar foo

function _import {
  source "$@"
}

echo -n _import bar:
_import bar
echo -n _import bar foo:
_import bar foo
Run Code Online (Sandbox Code Playgroud)

运行fooBash 脚本时,我得到以下输出,即./foo

source bar::0::
source bar foo::1:foo:
_import bar::1:bar:
_import bar foo::1:foo:
Run Code Online (Sandbox Code Playgroud)

以下是我的问题:

  1. 当我source_import函数中调用 Bash 的命令而不是直接调用时,为什么会有所不同?
  2. 如何规范 Bashsource命令的行为?

我在 Fedora 版本 20 上使用 Bash 版本 4.2.47(1)-release。

cuo*_*glm 6

问题是因为source在当前环境中执行文件。而在 中bash,如果没有提供位置参数,则它们不变。来自bash Bourne Shell Builtins手册页:

. (一段时间)

. 文件名 [参数]

从当前 shell 上下文中的 filename 参数读取和执行命令。如果文件名不包含斜杠,则使用 PATH 变量来查找文件名。当 Bash 不处于 POSIX 模式时,如果在 $PATH 中找不到文件名,则搜索当前目录。如果提供了任何参数,它们将在执行 filename 时成为位置参数。否则位置参数不变。返回状态是最后执行的命令的退出状态,如果没有执行命令,则为零。如果找不到文件名或无法读取文件名,则返回状态为非零。这个内置函数等同于 source。

POSIX 定义了sourcedotin 的同义词bash):

shell 应从当前环境中的文件执行命令。

它还解释了 KornShell 版本的 dot 可以采用可选参数,这些参数设置为位置参数:

dot 的 KornShell 版本采用设置为位置参数的可选参数。这是一个有效的扩展,它允许点脚本与函数的行为相同。

所以当你调用source bar内部_import函数时,你不提供任何位置参数,所以它们是不变的。它们与_import函数作用域相同,$@包含bar$#1(因为你运行_import bar)。

当您source bar_import函数作用域之外调用时,它与全局作用域(或foo脚本)相同。在这种情况下,因为您 run ./foo,所以您在foo没有任何参数的情况下运行,$@为 null 且$#为零。


Tim*_*ske 5

Gnouc 的回答解释了我的第一个问题:“为什么我从 _import 函数调用 Bash 的源命令而不是直接调用它会有所不同?”

关于我的第二个问题:“如何规范 Bash 源命令的行为?”

我想我找到了以下答案:

通过将_import函数更改为:

function _import {
  local -r file="$1"
  shift
  source "$file" "$@"
}
Run Code Online (Sandbox Code Playgroud)

运行fooBash 脚本时,我得到以下输出,即./foo

source bar::0::
source bar foo::1:foo:
_import bar::0::
_import bar foo::1:foo:
Run Code Online (Sandbox Code Playgroud)

我的问题和这个答案背后的基本原理:“导入的”Bash 脚本应该能够通过 Bash 的位置参数和特殊参数来评估它自己的参数集,即使在导入时没有给出任何参数。任何可以传递给导入 Bash 脚本的参数不得隐式传递给导入的 Bash 脚本。