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

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

给出以下可获取的barBash 脚本...

echo :$#:"$@":

...以及以下可执行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

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

source bar::0::
source bar foo::1:foo:
_import bar::1:bar:
_import bar foo::1:foo:

这是我的问题:

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

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

答案1

问题是因为source在当前环境中执行文件。在 中bash,如果未提供位置参数,则它们不会更改。从bash Bourne Shell 内置函数手册页:

。 (一段时间)

。文件名[参数]

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

POSIX 定义(是insource的同义词):dotbash

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

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

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

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

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

答案2

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

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

我想我找到了以下答案:

通过将_import函数更改为:

function _import {
  local -r file="$1"
  shift
  source "$file" "$@"
}

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

source bar::0::
source bar foo::1:foo:
_import bar::0::
_import bar foo::1:foo:

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

相关内容