我正在一个使用大量此类命令的平台上工作:
ssh [email protected] 'curl http://some_server/script.sh | bash'
这对于远程执行脚本来说确实非常简洁和方便,但是,我看不到获取脚本输出/退出代码的方法。任何人都可以想出一些办法来确保脚本已正确执行(从启动 ssh 的主机的角度来看)。
答案1
正如 @Zoredache 指出的那样,ssh
将远程命令的状态作为其自己的退出状态进行中继,因此错误检测可以通过 SSH 透明地进行。但是,您的示例中有两个重要点需要特别考虑。
首先,curl
它往往非常宽容,将许多异常情况视为成功。例如,curl http://serverfault.com/some-non-existent-url-that-returns-404
实际上退出状态为 0。我发现这种行为违反直觉。要将这些情况视为错误,我喜欢使用以下标志-fsS
:
- 当发生故障时,该
--fail
标志会抑制输出,这样bash
就不会有机会像代码一样执行 Web 服务器的 404 错误页面。 - 这些
--silent --show-error
标志一起提供了合理数量的错误报告。--silent
抑制所有来自的评论curl
。--show-error
重新启用错误消息,这些消息将被发送到 STDERR。
其次,你有一个管道,这意味着第一个或第二个命令都可能发生故障。从关于bash 中的管道(1):
管道的返回状态是最后一个命令的退出状态,除非
pipefail
启用了该选项(请参阅内置集合)。如果pipefail
启用,管道的返回状态是最后一个(最右边)以非零状态退出的命令的值,如果所有命令都成功退出,则返回零。
附注:bash
文档之所以相关,不是因为您通过管道连接到bash
,而是因为(我假设)它是您的远程用户的登录 shell,因此是解释远程命令行并处理管道执行的程序。如果用户有不同的登录 shell,请参阅该 shell 的文档。
举个具体的例子,
( echo whoami ; false ) | bash
echo $?
输出结果
login
0
说明bash
管道末端的 会屏蔽 返回的错误状态 ,false
只要 成功执行 就会返回0 whoami
。
相比之下,
set -o pipefail
( echo whoami ; false ) | bash
echo $?
产量
login
1
以便报告管道前半部分出现故障。
综上所述,解决方案应该是
ssh [email protected] 'set -s pipefail ; curl -fsS http://some_server/script.sh | bash'
这样,如果以下任何一个返回非零,您将获得非零的退出状态:
ssh
- 远程登录 shell
curl
bash
管道末端
此外,如果curl -fsS
检测到异常的 HTTP 状态代码,那么它将:
- 抑制其 STDOUT,这样就不会有任何内容通过管道传输
bash
到执行 - 返回一个非零值,该值被正确传播
- 将一行诊断消息打印到其 STDERR,该消息也会一直传播
答案2
那真是太糟糕了。如果您想要远程执行,请使用可以正确执行远程执行的工具,例如 func 或 mcollective。
答案3
当 SSH 返回时,它应该从脚本发出退出代码。
尝试一下。您应该会看到返回ssh user@host 'echo "exit 2" | bash' ; echo $?
的值。2
只需在脚本中写入大量良好的错误检查,并确保脚本退出时出现有用的错误和退出代码。确保您的脚本对任何错误返回非零退出代码。
答案4
这里有几个您可以尝试的选项。
另一种方法是让 echo $? 在脚本执行后立即运行,
# ssh [email protected] 'curl http://some_server/script.sh | bash; echo $?'
这将为您提供脚本的退出状态。或仅在退出状态为 0 时提供文本
# ssh [email protected] 'curl http://some_server/script.sh | bash && echo "Script Completed!"'
成功执行后将显示“脚本完成!”。或仅在退出状态不为 0 时显示错误代码
# ssh [email protected] 'curl http://some_server/script.sh | bash || echo "Script Failed: $?"'
这将显示“脚本失败:”正如@Zoredache 所建议的,在脚本中添加多个退出状态将有助于您识别出了什么问题。