Shell

通過 ssh 執行的 bash 腳本返回不正確的退出程式碼 0

  • December 19, 2018

我正在嘗試自動化一個過程,該過程涉及通過 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它似乎正在消失。它不會exitsh不會抱怨5: command not foundssh也不會抱怨。

在第三個測試中,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'

  1. 本地 shell 刪除單引號(引號刪除);
  2. ssh客戶端獲取參數、hostsh-cexit 5它將它們連接成一個字元串並將其發送到遠端主機;
  3. 在遠端主機上,ssh呼叫 shell 並將字元串傳遞給它sh -c exit 5
  4. 遠端 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

引用自:https://unix.stackexchange.com/questions/489737