Bash:为什么远程运行时会忽略换行符后的别名?

Ole*_*nge 4 bash alias newlines

在 Bash 中,我运行:

alias myalias='echo foo
echo bar
echo baz'
myalias
Run Code Online (Sandbox Code Playgroud)

返回:

foo
bar
baz
Run Code Online (Sandbox Code Playgroud)

但:

ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
Run Code Online (Sandbox Code Playgroud)

返回:

foo
Run Code Online (Sandbox Code Playgroud)

为什么?

Bru*_*uce 5

我想你发现了 Bash 的一个错误。此错误特定于 option -c

远程运行与你的多行别名问题无关。您可以在本地 bash 中尝试。但不是在 bash 脚本或交互式 bash 中,请尝试使用-c选项,如下所示

bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
Run Code Online (Sandbox Code Playgroud)

与您的问题相同的输出。仅foo打印。

要获得正确的(预期的)输出,您必须至少在 之后再添加一行myalias,正如@cuonglm 建议的那样。

bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"
Run Code Online (Sandbox Code Playgroud)

为什么会这样?为什么myalias帮助后多一行?

我只想说这没有意义。Bash 中没有任何文档解释或提到这种情况,一点也不。它不应该以这种方式运行。这是一个错误。阅读代码后,您将确定这一点。

回到第一个有问题的命令。这次不要改变任何东西,只需用 "ONESHOT" undefined重新编译 bash ,然后你就会得到正确的(预期的)输出。是的,你没听错,因为不同的编译时配置,该命令有两种不同的行为。

定义ONESHOT与否会导致在 Bash 代码中为-c "command". 如果undefine ONESHOT,-c "command"将运行正常的代码路由,这是几乎所有bash执行的代码路由,例如交互命令和bash脚本。但是如果定义了ONESHOT,-c "command"就会运行另一条专门为它设计的特定路由,通过避免fork来提高它的性能。

对于这种情况,正常且最常用的方式可以给出正确的输出,而特定方式则不能。我认为不一致的行为不是 Bash 作者想要的。至于哪种行为是对的,我倾向于认为正常的方式是对的。

关于这个错误的一些细节

以下代码段与该错误有关。它来自文件 builtins/evalstring.c 中的函数 parse_and_execute()

while (*(bash_input.location.string))
  {
    ...
  }
Run Code Online (Sandbox Code Playgroud)

while循环将按行运行,在一个循环中处理一行。在 read 之后myalias,命令中的最后一行(见上文),条件 inwhile将变为假。myalias扩展为三行回声,但在这个循环中只处理了一个回声;另外两个回声将在下一个循环中处理,但是......没有另一个循环。

如果myalias在 read之后再添加一行myalias,则条件 inwhile将保持为真,因此另外两个 echo 将有机会在下一个循环中运行。最后一行 aftermyalias将在处理完所有扩展的回声后myalias处理。

更新

忘记说这个问题涉及的Bash版本了,就是

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Run Code Online (Sandbox Code Playgroud)