我有一個本地腳本,想在多個遠端伺服器上運行。我使用的命令語法是:
ssh <remote_server> "bash -s" < ./local_script.sh
這工作得很好,甚至可以讓我將參數傳遞給本地腳本.sh。但是,我真的很想將輸入檔傳遞給它,如下所示:
local_script.sh < local_file.txt
結合這兩個語句可以得到:
ssh <remote_server> "bash -s" < ./local_script.sh < local_file.txt
當我運行這個時,我得到
bash: line 1: FOO: command not found
bash: line 2: BAR: command not found
bash: line 3: BAZ: command not found
...
其中 FOO、BAR、BAZ 等是每行的第一個單字本機檔案.txt:
FOO FOO_PARAM1
BAR BAR_PARAM1
BAZ BAZ_PARAM2
...
所以,看起來"bash -s"
遠端伺服器正在解釋本機檔案.txt作為腳本文件,而不是輸入文件本地腳本.sh。有什麼方法可以解決這個問題(除了創建包裝腳本之外)?
答案1
雖然ssh
提供了兩個獨立的輸出流(用於 stdout 和 stderr),但它只提供一個輸入流(stdin)。因此,您需要透過不同的機制傳遞腳本內容和輸入檔。
例如,一個透過變量,一個透過 stdin:
LC_CODE=$(cat local_script.sh) ssh host 'eval "$LC_CODE"' < input
(假設您的ssh
客戶端傳遞LC_*
變數(SendEnv
in ssh_config
)並且sshd
伺服器接受它們(AcceptEnv
in sshd_config
))
或簡單地將 的內容local_script.sh
作為遠端 shell 程式碼傳遞,假設遠端使用者的登入 shell 是該腳本語法的正確選擇:
ssh host "$(cat local_script.sh)" < input
或將代碼和輸入連接到ssh
的 stdin,如下所示:
{
echo '{'; cat local_script.sh; echo '}; exit'
cat input
} | ssh host 'bash -s some arguments'
這裡使用bash
becausebash
將負責一次一個位元組地讀取輸入,以免讀取超過該}; exit
行,而並非所有其他shell都這樣做。
或如果sed
遠端主機上是 GNU sed
:
echo '#END-OF-SCRIPT' | cat local_script.sh - input |
ssh host 'set some arguments; eval "$(sed -u "/^#END-OF-SCRIPT/q")"'
(此處由遠端使用者的登入 shell 評估程式碼並假設它類似於 Bourne)
答案2
這是使用 BASH 'here-strings' 運算子 ('<<<') 的更優雅的解決方案
ssh -t host "/bin/bash <(base64 --decode <<<'$(base64 < script)' )"
IT 取得腳本,將其轉換為 Base64 字串,在 SSH 命令中將其轉換回來並將其作為管道檔案名稱提供給 bash。
對於建立互動式 shell 的腳本,請在“管道檔案名稱”前面加上“--rcfile”
ssh -t host "/bin/bash --rcfile <(base64 --decode <<<'$(base64 < script)' )"
更多關於... https://antofthy.gitlab.io/info/apps/ssh_remote_commands.txt
答案3
這很有效,甚至可以讓您將多個文件的內容傳遞給腳本:
ssh host "bash -s -- '$(cat local_file_1.txt)' '$(cat local_file_2.txt)'" < local_script.sh
解釋是它將在登入時ssh
執行bash -s
,腳本參數是 後的所有內容--
。$1
的內容也將如此,local_file_1.txt
的$2
內容也將如此local_file_2.txt
。bash -s
從來自 的 stdin 讀取命令< local_script.sh
。