通过 ssh 评估命令替换时退出状态错误?

通过 ssh 评估命令替换时退出状态错误?

我有以下简单的 shell 命令,我预计它会失败,而且它在我的本地也失败了:

$ DIR=$(false) && echo ok || echo fail
fail
$ sh -c 'DIR=$(false) && echo ok || echo fail'
fail

但是当我通过 ssh 传递此命令时,它不再按预期工作:

$ ssh user@host sh -c 'DIR=$(false) && echo ok || echo fail'
ok

所以我不太清楚问题出在哪里。我已经使用撇号来避免变量扩展过早

发生了什么事,以及如何根据变量赋值返回的退出代码使命令替换正常工作?


我在以下示例中发现了另一个异常:

$ ssh user@host sh -c 'echo 1; echo 2;'

2

即仅打印 2,而不是同时打印 1 和 2。

答案1

我认为问题在于涉及的炮弹:

  1. 处理命令的本地 shell ssh
  2. ssh在目标主机中调用来处理命令的远程shell ;
  3. sh -c远程命令中调用的附加 shell 。

引文有两个作用:

  • 它们停止本地 shell 评估本地 shell 中的任何变量 (1);
  • 他们确保遥控器只有一个参数sh -c

因此ssh可以看到四个参数:user@hostsh-c所需的命令。但是,引号被剥夺在构建第四个参数时,当远程 shell (2) 收到其参数时,它会自我解释$(false),并在调用子 shell (3) 之前在其自己的环境中设置完成代码。

因此,附加 shell (3) 看到的DIR= && echo ok || echo failDIR=一个完全有效、无错误的命令,因此产生ok分支。

您可以在以下位置看到相同的效果:

sh -c 'sh -c "DIR=$(false) && echo ok || echo not"'

或者:

sh -c "sh -c 'DIR=$(false) && echo ok || echo not'"

\在这两种情况下,如果你在 之前放置$,它都可以“正常”工作,因为这会阻止第二个 shell 扩展$(false)。我使用双引号使机制更清晰 - 如果你喜欢,可以采用相当曲折的方式:

sh -c 'sh -c '\''DIR=$(false) && echo ok || echo not'\'

Richard 的示例有效,因为所有扩展都在单个远程 shell 中完成,并且首字母false;对后续命令没有影响。这与他的链接中讨论的主题无关。

答案2

这应该可以按预期工作:

ssh user@host 'DIR=$(false) && echo ok || echo fail'

看起来,当按上述方式调用时,如果 shell 管道中的第一个语句是变量赋值,它将不起作用。

奇怪的是,这也按预期工作:

ssh user@host sh -c 'false; DIR=$(false) && echo ok || echo fail'

我不完全确定这里发生了什么,但它可能与这个问题有关: https://unix.stackexchange.com/questions/126938/why-is-setting-a-variable-before-a-command-legal-in-bash

相关内容