Ich habe ein lokales Skript, das ich auf mehreren Remote-Servern ausführen möchte. Die von mir verwendete Befehlssyntax ist:
ssh <remote_server> "bash -s" < ./local_script.sh
Das funktioniert gut und ich kann sogar Parameter übergeben anlocal_script.sh. Ich würde ihm jedoch wirklich gerne eine Eingabedatei übergeben, wie in:
local_script.sh < local_file.txt
Die Kombination dieser beiden Aussagen ergibt:
ssh <remote_server> "bash -s" < ./local_script.sh < local_file.txt
Wenn ich das ausführe, bekomme ich
bash: line 1: FOO: command not found
bash: line 2: BAR: command not found
bash: line 3: BAZ: command not found
...
wobei FOO, BAR, BAZ usw. das erste Wort in jeder Zeile sindlokale_datei.txt:
FOO FOO_PARAM1
BAR BAR_PARAM1
BAZ BAZ_PARAM2
...
Es sieht also so aus, als ob "bash -s"
der Remote-Server interpretiertlokale_datei.txtals Skriptdatei statt als Eingabedatei fürlocal_script.sh. Gibt es eine Möglichkeit, dies zu beheben (außer ein Wrapper-Skript zu erstellen)?
Antwort1
Während ssh
es zwei separate Ausgabeströme (für stdout und stderr) bereitstellt, speist es nur einen Eingabestrom (stdin). Sie müssen also den Skriptinhalt und die Eingabedatei über unterschiedliche Mechanismen übergeben.
Beispielsweise einmal über eine Variable, einmal über stdin:
LC_CODE=$(cat local_script.sh) ssh host 'eval "$LC_CODE"' < input
(vorausgesetzt, sowohl Ihr ssh
Client übergibt die LC_*
Variable ( SendEnv
in ssh_config
) als auch der sshd
Server akzeptiert sie ( AcceptEnv
in sshd_config
))
Oder übergeben Sie den Inhalt einfach local_script.sh
als Remote-Shell-Code, vorausgesetzt, die Login-Shell des Remote-Benutzers ist die richtige für die Syntax dieses Skripts:
ssh host "$(cat local_script.sh)" < input
Oder verketten Sie den Code und die Eingabe, die an ssh
die Standardeingabe von gesendet werden, wie folgt:
{
echo '{'; cat local_script.sh; echo '}; exit'
cat input
} | ssh host 'bash -s some arguments'
Hier sorgt die Verwendung von „ bash
weil“ dafür, dass die Eingabe Byte für Byte gelesen wird, um nicht über diese Zeile hinaus zu lesen, während dies nicht bei allen anderen Shells der Fall ist.bash
}; exit
Oder wenn sed
sich auf dem Remote-Host GNU befindet sed
:
echo '#END-OF-SCRIPT' | cat local_script.sh - input |
ssh host 'set some arguments; eval "$(sed -u "/^#END-OF-SCRIPT/q")"'
(hier wird der Code von der Login-Shell des Remote-Benutzers ausgewertet und es wird angenommen, dass er Bourne-ähnlich ist)
Antwort2
Hier ist eine elegantere Lösung mit dem BASH-Operator „here-strings“ ('<<<').
ssh -t host "/bin/bash <(base64 --decode <<<'$(base64 < script)' )"
Es nimmt das Skript, konvertiert es in eine Base64-Zeichenfolge, konvertiert es im SSH-Befehl zurück und gibt es als Pipe-Dateinamen an die Bash weiter.
Damit ein Skript eine interaktive Shell einrichten kann, muss dem „Pipe-Dateinamen“ das Präfix „--rcfile“ vorangestellt werden.
ssh -t host "/bin/bash --rcfile <(base64 --decode <<<'$(base64 < script)' )"
Mehr dazu... https://antofthy.gitlab.io/info/apps/ssh_remote_commands.txt
Antwort3
Dies funktioniert gut und ermöglicht Ihnen sogar, den Inhalt mehrerer Dateien an das Skript zu übergeben:
ssh host "bash -s -- '$(cat local_file_1.txt)' '$(cat local_file_2.txt)'" < local_script.sh
Die Erklärung ist, dass beim Anmelden ssh
ausgeführt wird bash -s
, wobei die Skriptargumente alles nach sind --
. Daher $1
wird der Inhalt von sein local_file_1.txt
und $2
wird der Inhalt von sein local_file_2.txt
. bash -s
liest Befehle von stdin, das von kommt < local_script.sh
.