通過 ssh 執行的 bash 腳本返回不正確的退出程式碼 0
我正在嘗試自動化一個過程,該過程涉及通過 ssh 在各種機器上執行腳本。擷取輸出和返回程式碼(用於檢測錯誤)至關重要。
明確設置退出程式碼按預期工作:
~$ ssh host exit 5 && echo OK || echo FAIL FAIL
但是,如果有一個 shell 腳本發出不干淨退出的信號,ssh 總是返回 0(由字元串執行模擬的腳本):
~$ ssh host sh -c 'exit 5' && echo OK || echo FAIL OK
在互動式 shell 中在主機上執行相同的腳本就可以了:
~$ sh -c 'exit 5' && echo OK || echo FAIL FAIL
我很困惑為什麼會發生這種情況。如何告訴 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
結果如下:
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
在第一次測試和第二次測試中,似乎沒有像我們預期的那樣
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
例如,在這個答案中解釋了這裡發生的事情:
ssh
執行遠端 shell 並向其傳遞一個字元串,而不是參數列表。當我們執行
ssh host sh -c 'exit 5'
:
- 本地 shell 刪除單引號(引號刪除);
ssh
客戶端獲取參數、host
、sh
和-c
。exit 5
它將它們連接成一個字元串並將其發送到遠端主機;- 在遠端主機上,
ssh
呼叫 shell 並將字元串傳遞給它sh -c exit 5
;- 遠端 shell 呼叫
sh
並將-c
選項exit
作為命令字元串和命令名稱5
傳遞給它。請注意,如果我們在 之後添加單詞
exit 5
,它們只是sh
作為進一步的參數傳遞給它們 - 沒有與它們相關的錯誤,它們未被 shell 辨識:$ ssh test sh -x -c 'exit 5' a b c; echo $? + exit 0
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
為了
sh -c 'command'
按預期在遠端主機上執行,我們必須確保也正確地發送引號:$ ssh test "sh -x -c 'exit 5'"; echo $? + exit 5 5
為了明確引用整個遠端命令與我們目前的問題無關,我們可以寫:
$ ssh test sh -x -c "'exit 5'"; echo $? + exit 5 5
用反斜杠轉義內部引號,而不是引用兩次,也可以。
關於命令
ssh host sh -c ':; exit 5'
的註釋(從評論到您的問題)。它的作用是:$ ssh test sh -x -c ':; exit 5'; echo $? + : 5
也就是說,
exit 5
由外殼執行,而不是由sh
. 同樣,讓sh
退出所需的程式碼:$ ssh test sh -x -c "':; exit 5'"; echo $? + : + exit 5 5