Her*_*ann 7 shell ssh exit-status
我正在尝试自动化一个过程,该过程涉及通过 ssh 在各种机器上运行脚本。捕获输出和返回代码(用于检测错误)至关重要。
设置退出代码按预期工作:
~$ ssh host exit 5 && echo OK || echo FAIL
FAIL
Run Code Online (Sandbox Code Playgroud)
但是,如果有 shell 脚本发出不干净的退出信号,ssh 总是返回 0(通过字符串执行模拟的脚本):
~$ ssh host sh -c 'exit 5' && echo OK || echo FAIL
OK
Run Code Online (Sandbox Code Playgroud)
在交互式 shell 中的主机上运行完全相同的脚本工作正常:
~$ sh -c 'exit 5' && echo OK || echo FAIL
FAIL
Run Code Online (Sandbox Code Playgroud)
我很困惑为什么会发生这种情况。如何告诉 ssh 传播 bash 的返回码?我可能不会更改远程脚本。
我使用的是公钥认证,私钥是解锁的——不需要用户交互。所有系统都是 Ubuntu 18.04。应用程序版本是:
OpenSSH_7.6p1 Ubuntu-4ubuntu0.1, OpenSSL 1.0.2n 7 Dec 2017
GNU bash, Version 4.4.19(1)-release (x86_64-pc-linux-gnu)
注意:这个问题与这些看似相似的问题不同:
我可以使用您使用的命令复制它,并且我可以通过将远程命令括在引号中来解决它。这是我的测试用例:
#!/bin/bash -x
echo 'Unquoted Test:'
ssh evil sh -x -c exit 5 && echo OK || echo FAIL
echo 'Quoted Test 1:'
ssh evil sh -x -c 'exit 5' && echo OK || echo FAIL
echo 'Quoted Test 2:'
ssh evil 'sh -x -c "exit 5"' && echo OK || echo FAIL
Run Code Online (Sandbox Code Playgroud)
结果如下:
bash-[540]$ bash -x test.sh
+ echo 'Unquoted Test:'
Unquoted Test:
+ ssh evil sh -x -c exit 5
+ exit
+ echo OK
OK
+ echo 'Quoted Test 1:'
Quoted Test 1:
+ ssh evil sh -x -c 'exit 5'
+ exit
+ echo OK
OK
+ echo 'Quoted Test 2:'
Quoted Test 2:
+ ssh evil 'sh -x -c "exit 5"'
+ exit 5
+ echo FAIL
FAIL
Run Code Online (Sandbox Code Playgroud)
在第一次测试和第二次测试中,似乎5
没有exit
像我们期望的那样传递给它。它似乎正在消失。它不会exit
,sh
不会抱怨5: command not found
,ssh
也不会抱怨。
在第三个测试中,exit 5
在更大的命令中引用以在远程主机上运行,与第二个测试相同。这可确保5
传递给exit
,并且两者都作为-c
选项执行sh
。第二个和第三个测试之间的区别在于,整个命令和参数集被发送到作为单个命令参数引用到的远程主机ssh
。
正如您已经拥有的答案中所述,遥控器sh
没有执行exit 5
. 只是exit
:
$ ssh test sh -x -c 'exit 5'; echo $?
+ exit
0
Run Code Online (Sandbox Code Playgroud)
例如,在这个答案中解释了这里发生的事情:
ssh
执行一个远程 shell并向它传递一个字符串,而不是一个参数列表。
当我们执行ssh host sh -c 'exit 5'
:
ssh
客户端获取的参数host
,sh
,-c
,和exit 5
。它将它们连接成一个字符串并将其发送到远程主机;ssh
调用一个 shell 并将字符串传递给它sh -c exit 5
;sh
并将其传递给-c
选项,exit
作为命令串,并5
作为命令名。请注意,如果我们在 之后添加单词exit 5
,它们只是sh
作为进一步的参数传递给- 没有与 shell 无法识别它们相关的错误:
$ ssh test sh -x -c 'exit 5' a b c; echo $?
+ exit
0
Run Code Online (Sandbox Code Playgroud)
strace
确认这5
不是给 , 的命令字符串的一部分sh
;这是一个论点:
$ ssh test strace -e execve sh -c 'exit 5'; echo $?
execve("/usr/bin/sh", ["sh", "-c", "exit", "5"], 0x7ffc0d744c38 /* 14 vars */) = 0
+++ exited with 0 +++
0
Run Code Online (Sandbox Code Playgroud)
为了sh -c 'command'
按预期在远程主机上执行,我们还必须确保正确地将引号发送给它:
$ ssh test "sh -x -c 'exit 5'"; echo $?
+ exit 5
5
Run Code Online (Sandbox Code Playgroud)
为了明确引用整个远程命令与我们当前的问题无关,我们可以这样写:
$ ssh test sh -x -c "'exit 5'"; echo $?
+ exit 5
5
Run Code Online (Sandbox Code Playgroud)
用反斜杠转义内部引号,而不是引用两次,也可以。
关于命令ssh host sh -c ':; exit 5'
的注释(从评论到您的问题)。它的作用是:
$ ssh test sh -x -c ':; exit 5'; echo $?
+ :
5
Run Code Online (Sandbox Code Playgroud)
也就是说,exit 5
由外壳程序执行,而不是由sh
. 再次,让sh
退出所需的代码:
$ ssh test sh -x -c "':; exit 5'"; echo $?
+ :
+ exit 5
5
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5573 次 |
最近记录: |