Bash

如何 ssh 並執行包含在 if else 中的多個命令?

  • January 25, 2020

我想使用 ssh 從各種伺服器擷取一些數據並在某些條件下執行一些命令。

我不想這樣做:

if ssh $host test -f /file; then
 # If file exists
 var=$(ssh $host long pipeline)
else
 # if it doesn't
 var=$(ssh $host another long pipeline)
fi

因為它會使過程更長。我想在遠端機器上執行 if else 。


我嘗試了幾種方法,但我沒有運氣。

var=$(ssh $host if [ -f /file ]\; then long pipeline1 \; else long pipeline2 \; fi)

基於這個答案,它可以工作,但 pipeline1 的最後一個命令假定它else和 pipeline2 的其餘部分作為它的參數。

command: can't read else: No such file or directory
...
command: can't read fi: No such file or directory

然後我嘗試了這個

var=$(ssh $host test -f /file \&\& pipeline1 \|\| pipeline2)

同樣, pipeline1 的最後一個命令被||視為其參數。


我也在下面嘗試過(基於this),它正在工作:

do_this () {
 if [ -f /file ]; then
   pipeline1
 else
   pipeline2
 fi
}
var=$(ssh $host "$(set); do_this")

然而,它會列印不影響我的變數的不需要的錯誤消息,但它對我的腳本來說很難看。

bash: line 1: BASHOPTS: readonly variable
bash: line 8: BASH_VERSINFO: readonly variable
bash: line 24: EUID: readonly variable
bash: line 71: PPID: readonly variable
bash: line 82: SHELLOPTS: readonly variable
bash: line 92: UID: readonly variable

有什麼建議麼?


更新

我想我必須包括我的管道是什麼,基本上它只是一堆文本處理:

cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-'

根據Jetchisel的回答,簡而言之,我必須用單引號括起我的命令。

var=$(ssh $host 'if [ -f /file ]; then cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-' ; else cat otherfile | ... ; fi'

我得到了tr: invalid option -- ';'tr當作;它的論據。


它使用heredoc

var=$(ssh $host <<-EOF
 if [ -f file ]; then
   pipeline1
 else
   pipeline2
 fi
EOF
)

然而,由於我在 sed 中使用的正則表達式,它破壞了 vim 的著色。我現在會接受heredoc作為答案。


更新 2:我相信我的問題不是sshpass 中的多個命令的重複,我的情況更具體,而另一個執行緒一般會問它。

在:

ssh host code

ssh實際上執行一個 shell(目標使用者的登錄 shell)來解釋您作為參數傳遞的程式碼。如果給出了多個參數,ssh 將它們與空格連接起來,並再次讓使用者的登錄 shellhost解釋它。

通常,您希望將單個程式碼參數傳遞給並確保它用ssh引號引起來,以確保本地 shell 不會進行擴展。

如果您知道遠端使用者的登錄 shell 類似於 Bourne/POSIX,那麼您所要做的就是:

var=$(ssh "$host" '
 if [ -f /file ]; then
   pipeline1
 else
   pipeline2
 fi'
)

如果要遠端解釋的程式碼必須在其中包含單引號,則需要將它們插入為'\''(保留單引號,插入帶引號的(帶反斜杠)單引號,重新輸入單引號)。

如果您不能保證遠端使用者的外殼,並且您不需要通過其標準輸入將數據傳遞給遠端命令(並且任何遠端命令都不會從其標準輸入讀取,除非它重定向到其他東西而不是 ssh 連接),您可以這樣做:

ssh "$host" sh << 'EOF'
 if [ -f /file ]; then
   pipeline1
 else
   pipeline2
 fi
EOF

通過引用第一個EOF,我們確保本地 shell 沒有在 here 文件中進行擴展。我們顯式呼叫sh以解釋其標準輸入上的程式碼,因此我們知道編寫腳本的語法。

這種方法還避免了必須轉義單引號。

您的

cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-'

可以寫成:

<file sed '/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'

所以這給了我們:

ssh "$host" '
 file=/path/to/some/file
 otherfile=/path/to/some/other/file

 if [ -f "$file" ]; then
   <"$file" sed '\''/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'\''
 else
   <"$otherfile" ...
 fi'

(如果遠端使用者的登錄 shell 是例如 csh、tcsh、fish、rc、es、akanga,其語法與 Bourne/POSIX-like 語法不同,這將不起作用)

或者:

ssh "$host" sh << 'EOF'
 file=/path/to/some/file
 otherfile=/path/to/some/other/file

 if [ -f "$file" ]; then
   <"$file" sed '/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'
 else
   <"$otherfile" ...
 fi
EOF

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