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

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

在 Bash 中我运行:

alias myalias='echo foo
echo bar
echo baz'
myalias

返回:

foo
bar
baz

但:

ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"

返回:

foo

为什么?

答案1

我认为你发现了 Bash 的一个错误。此错误特定于选项-c

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

bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"

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

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

bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"

为什么会这样呢?为什么help后面多了一行myalias

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

返回到第一个有问题的命令。这次什么都不用改,重新编译bash即可“ONESHOT”未定义,那么您将得到正确的(预期的)输出。是的,你没听错,由于不同的编译时配置,该命令有两种不同的行为。

无论是否定义ONESHOT,都会导致 Bash 代码中出现两条完全不同的路由-c "command"。如果取消定义 ONESHOT,-c "command"将运行正常的代码路由,这是几乎所有 bash 执行的代码路由,例如交互式命令和 bash 脚本。但如果定义了ONESHOT,-c "command"则会运行另一条专门为其设计的特定路线,以通过避免分叉来提高其性能。

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

有关此错误的一些详细信息

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

while (*(bash_input.location.string))
  {
    ...
  }

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

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

更新

忘了说这个问题涉及到的Bash版本是

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

答案2

解决方法(受@cuonglm启发):

ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='ls
echo foo
echo bar
echo baz'
myalias &&
true"

这将保留退出代码。然而true必须走上新的路线。

它仍然没有解释为什么。但它看起来越来越像一个错误。

相关内容