Wie verwende ich SSH, um ein Shell-Skript auf einem Remotecomputer auszuführen?

1221

Ich muss ein Shell-Skript (Windows / Linux) auf einem Remote-Computer ausführen.

Ich habe SSH auf Computer A und B konfiguriert. Mein Skript befindet sich auf Computer A, auf dem ein Teil meines Codes auf einem Remotecomputer, Computer B, ausgeführt wird.

Die lokalen und Remote-Computer können entweder Windows- oder Unix-basierte Systeme sein.

Gibt es eine Möglichkeit, dies mit plink / ssh auszuführen?

alpha_989
quelle
6
Die gleiche Frage ist bereits in serverfault enthalten: serverfault.com/questions/215756/… Es ist also wahrscheinlich sinnlos, diese Frage zu migrieren.
Sleske
9
Die Frage zu Server Fault hat jedoch nicht so viele Antworten. Vielleicht sollte diese Frage diese ersetzen.
Big McLargeHuge
5
Ich mag diese Antwort persönlich: unix.stackexchange.com/questions/87405/…
Mikevoermans
27
Darüber hinaus sollte es natürlich zum Thema gehören, da ssh ein erstklassiges Werkzeug für die Softwareentwicklung ist.
static_rtti
4
Kaffee- und SSH-Fragen haben bei SO nicht den gleichen Grad an Off-Topicness. Für die Wiedereröffnung gestimmt.
Vincent Cantin

Antworten:

1193

Wenn Maschine A eine Windows-Box ist, können Sie Plink (Teil von PuTTY ) mit dem Parameter -m verwenden und das lokale Skript auf dem Remote-Server ausführen.

plink root@MachineB -m local_script.sh

Wenn Maschine A ein Unix-basiertes System ist, können Sie Folgendes verwenden:

ssh root@MachineB 'bash -s' < local_script.sh

Sie sollten das Skript nicht auf den Remote-Server kopieren müssen, um es auszuführen.

Jason R. Coombs
quelle
11
Gibt es einen Vorteil bei der Verwendung der -sOption? Diese Manpage lässt mich glauben, dass sie Standardeingaben verarbeitet, wenn die Verarbeitungsoptionen fertig sind, unabhängig davon, ob sie -sverwendet werden oder nicht.
AeroNotAuto
79
sudoFühren Sie für ein Skript aus, das dies erfordert ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
Bradley.ayers
6
@ bradley.ayers denken Sie daran, den Befehl mit einem 'Leerzeichen' zu starten, um den Verlauf zu überspringen (PS müssen Sie HISTCONTROL=ignoreboth or ignorespace, damit es funktioniert)
ihrio
8
@ bradley.ayers In welchen Situationen würden Sie sudo benötigen, wenn Sie sich bereits als root anmelden?
Brian Schlenker
21
@Agostino, Sie können folgende Parameter hinzufügen: Die ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh Credits gehen vollständig zur Antwort von @chubbsondubs unten.
Yves Van Broekhoven
634

Dies ist eine alte Frage, und Jasons Antwort funktioniert gut, aber ich möchte Folgendes hinzufügen:

ssh user@host <<'ENDSSH'
#commands to run on remote host
ENDSSH

Dies kann auch mit su und Befehlen verwendet werden, für die Benutzereingaben erforderlich sind. (Beachten Sie den 'entkommenen Heredoc)

Bearbeiten: Da diese Antwort immer wieder Verkehr bekommt, würde ich dieser wunderbaren Verwendung von Heredoc noch mehr Informationen hinzufügen:

Sie können Befehle mit dieser Syntax verschachteln, und nur so scheint das Verschachteln zu funktionieren (auf vernünftige Weise).

ssh user@host <<'ENDSSH'
#commands to run on remote host
ssh user@host2 <<'END2'
# Another bunch of commands on another host
wall <<'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.secureftp-test.com <<'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH

Sie können tatsächlich ein Gespräch mit einigen Diensten wie Telnet, FTP usw. führen. Denken Sie jedoch daran, dass heredoc das stdin nur als Text sendet und nicht auf eine Antwort zwischen den Zeilen wartet

Bearbeiten: Ich habe gerade herausgefunden, dass Sie die Innenseiten mit Tabulatoren einrücken können, wenn Sie verwenden <<-END!

ssh user@host <<-'ENDSSH'
    #commands to run on remote host
    ssh user@host2 <<-'END2'
        # Another bunch of commands on another host
        wall <<-'ENDWALL'
            Error: Out of cheese
        ENDWALL
        ftp ftp.secureftp-test.com <<-'ENDFTP'
            test
            test
            ls
        ENDFTP
    END2
ENDSSH

(Ich denke das sollte funktionieren)

Siehe auch http://tldp.org/LDP/abs/html/here-docs.html

Yarek T.
quelle
4
Sie können ein wenig temporieren, indem Sie Zeilen hinzufügen wie: # $ (Schlaf 5)
Olivier Dulac
50
Beachten Sie, dass bei einfachen Anführungszeichen um den Terminator ( <<'ENDSSH') die Zeichenfolgen nicht erweitert und Variablen nicht ausgewertet werden. Sie können auch verwenden <<ENDSSHoder <<"ENDSSH"wenn Sie eine Erweiterung wünschen.
Maackle
3
Expectkann verwendet werden, wenn Sie interaktive Befehle wie FTP automatisieren müssen.
Programmaths
5
Beachten Sie, dass ich eine Pseudo-terminal will not be allocated because stdin is not a terminal.Nachricht hatte. Man muss ssh mit -t -tparams verwenden, um das zu vermeiden. Siehe diesen Thread auf SO
Buzut
8
Wenn Sie versuchen, die Syntax << - 'END' zu verwenden, stellen Sie sicher, dass Ihr Heredoc-Endbegrenzer mit TABs und nicht mit Leerzeichen eingerückt ist. Beachten Sie, dass Sie durch Kopieren / Einfügen aus Stackexchange Leerzeichen erhalten. Ändern Sie diese in Registerkarten, und die Einrückungsfunktion sollte funktionieren.
Fbicknel
249

Vergessen Sie auch nicht, Variablen zu maskieren, wenn Sie sie vom Zielhost abholen möchten.

Das hat mich in der Vergangenheit erwischt.

Zum Beispiel:

user@host> ssh user2@host2 "echo \$HOME"

druckt / home / user2 aus

während

user@host> ssh user2@host2 "echo $HOME"

druckt / home / user aus

Ein anderes Beispiel:

user@host> ssh user2@host2 "echo hello world | awk '{print \$1}'"

druckt "Hallo" richtig aus.

Dogbane
quelle
2
ssh user2@host 'bash -s' echo $HOME /home/user2 exit
Beachten
1
Nur um hinzuzufügen, dass in forSchleifen, die in einer sshSitzung ausgeführt werden, die Schleifenvariable nicht maskiert werden darf.
AlexeyDaryin
1
In vielen Situationen besteht die vernünftige Möglichkeit, Ihr letztes Beispiel zu reparieren, darin, ssh user2@host2 'echo hello world' | awk '{ print $1 }'das Awk-Skript lokal auszuführen. Wenn der Remote-Befehl eine immense Ausgabe erzeugt, möchten Sie natürlich vermeiden, alles auf den lokalen Server zurück zu kopieren. Im Übrigen vermeiden einfache Anführungszeichen um den Remote-Befehl, dass ein Escapezeichen erforderlich ist.
Tripleee
151

Dies ist eine Erweiterung der Antwort von YarekT, Inline-Remote-Befehle mit der Übergabe von ENV-Variablen vom lokalen Computer an den Remote-Host zu kombinieren, damit Sie Ihre Skripte auf der Remote-Seite parametrisieren können:

ssh user@host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'
  # commands to run on remote host
  echo $ARG1 $ARG2
ENDSSH

Ich fand das außerordentlich hilfreich, indem ich alles in einem Skript aufbewahrte, damit es sehr gut lesbar und wartbar ist.

Warum das so ist. ssh unterstützt die folgende Syntax:

ssh user @ host remote_command

In bash können wir Umgebungsvariablen angeben, die definiert werden sollen, bevor ein Befehl in einer einzelnen Zeile ausgeführt wird:

ENV_VAR_1 = 'Wert1' ENV_VAR_2 = 'Wert2' Bash -c 'Echo $ ENV_VAR_1 $ ENV_VAR_2'

Das macht es einfach, Variablen vor dem Ausführen eines Befehls zu definieren. In diesem Fall ist Echo unser Befehl, den wir ausführen. Alles vor dem Echo definiert Umgebungsvariablen.

Also kombinieren wir diese beiden Funktionen und die Antwort von YarekT, um Folgendes zu erhalten:

ssh user @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

In diesem Fall setzen wir ARG1 und ARG2 auf lokale Werte. Senden Sie alles nach user @ host als remote_command. Wenn der Remotecomputer den Befehl ARG1 und ARG2 ausführt, werden die lokalen Werte dank der lokalen Befehlszeilenauswertung festgelegt, die Umgebungsvariablen auf dem Remoteserver definiert, und führt dann den Befehl bash -s unter Verwendung dieser Variablen aus. Voila.

Chubbsondubs
quelle
1
Beachten Sie, dass Sie - verwenden können, wenn Sie Argumente wie -a übergeben möchten. zB 'ssh user @ host - -a foo bar' bash -s '<script.sh'. Und die Argumente können auch nach der Umleitung gehen, z. B. 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
Gaoithe
8
Wenn einer der env var-Werte Leerzeichen enthält, verwenden Sie:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle
Wie ich in einem anderen Kommentar geschrieben habe, besteht der eigentliche Sinn der Verwendung -sdarin, Argumente auf Skripte anwenden zu können, die über stdin bezogen werden. Ich meine, Sie können es auch weglassen, wenn Sie es nicht verwenden. Wenn Sie es verwenden, gibt es keinen Grund, Umgebungsvariablen zu verwenden:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL
104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Daraufhin werden Sie zur Eingabe des Kennworts aufgefordert, es sei denn, Sie haben den öffentlichen Schlüssel Ihres hostA-Benutzers in die Datei authorized_keys im Verzeichnis des Benutzers .ssh kopiert. Dies ermöglicht eine kennwortlose Authentifizierung (sofern dies in der Konfiguration des SSH-Servers als Authentifizierungsmethode akzeptiert wird).

Vinko Vrsalovic
quelle
3
Hat dich gewählt. Dies ist eine gültige Lösung. Natürlich müssen die Schlüssel geschützt werden, aber sie können auch wie ein Passwort über die Serverseite ungültig gemacht werden.
Willasaywhat
8
Ich denke nicht, dass dies die Frage beantwortet. Das Beispiel zeigt, wie ein Remote-Befehl ausgeführt wird, nicht jedoch, wie ein lokales Skript auf einem Remote-Computer ausgeführt wird.
Jason R. Coombs
Sie sind sich nicht sicher, aber können Sie Ihr Skript nicht auf hostA weiterleiten, um es mit dieser Methode auf hostB auszuführen?
nevets1219
27

Ich habe angefangen, Fabric für anspruchsvollere Operationen zu verwenden. Fabric erfordert Python und einige andere Abhängigkeiten, jedoch nur auf dem Client-Computer. Der Server muss nur ein SSH-Server sein. Ich finde, dass dieses Tool viel leistungsfähiger ist als an SSH übergebene Shell-Skripte und die Mühe wert ist, eingerichtet zu werden (insbesondere, wenn Sie gerne in Python programmieren). Fabric verwaltet das Ausführen von Skripten auf mehreren Hosts (oder Hosts bestimmter Rollen), erleichtert idempotente Vorgänge (z. B. das Hinzufügen einer Zeile zu einem Konfigurationsskript, jedoch nicht, wenn es bereits vorhanden ist) und ermöglicht die Erstellung komplexerer Logik (z. B. Python) Sprache kann bieten).

Jason R. Coombs
quelle
11

Versuche zu rennen ssh user@remote sh ./script.unx.

Jeremy
quelle
8
Dies funktioniert nur, wenn sich das Skript im Standardverzeichnis (Home) auf der Fernbedienung befindet. Ich denke, die Frage ist, wie man ein lokal auf der Fernbedienung gespeichertes Skript ausführt.
Metasim
1
ssh Benutzername @ ip "chmod + x script.sh" <br/> ssh Benutzername @ ip "Pfad zur sh-Datei im Remote-Host"
Mani Deepak
10
cat ./script.sh | ssh <user>@<host>
cglotr
quelle
1
Sehr interessant ...
Noah Broyles
8

Angenommen, Sie möchten dies automatisch von einem "lokalen" Computer aus tun, ohne sich manuell bei dem "Remote" -Computer anzumelden, sollten Sie sich eine TCL-Erweiterung ansehen, die als "Expect" bezeichnet wird. Sie ist genau für diese Art von Situation konzipiert. Ich habe auch einen Link zu einem Skript zum Anmelden / Interagieren über SSH bereitgestellt.

https://www.nist.gov/services-resources/software/expect

http://bash.cyberciti.biz/security/expect-ssh-login-script/

Sauerstoff
quelle
5

Ich verwende dieses, um ein Shell-Skript auf einem Remote-Computer auszuführen (getestet auf / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh
Ist Ma
quelle
3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

Es wird dringend empfohlen, die Umgebungsdatei (.bashrc / .bashprofile / .profile) als Quelle zu verwenden. bevor Sie etwas auf einem Remote-Host ausführen, da Umgebungsvariablen für Ziel- und Quellhosts möglicherweise verzögert sind.

Ankush Sahu
quelle
Dies erklärt nicht, wie ein lokales Skript auf den Remote-Host verschoben wird.
Kirelagin
2

Wenn Sie einen Befehl wie diesen ausführen möchten temp=`ls -a` echo $temp in `` möchten, werden Fehler verursacht.

Der folgende Befehl löst dieses Problem ssh user@host ''' temp=`ls -a` echo $temp '''

Jinmiao Luo
quelle
1

Die Antwort hier ( https://stackoverflow.com/a/2732991/4752883 ) funktioniert hervorragend, wenn Sie versuchen, ein Skript auf einem Remote-Linux-Computer mit plinkoder auszuführen ssh. Es funktioniert, wenn das Skript mehrere Zeilen enthält linux.

** Wenn Sie jedoch versuchen, ein Batch-Skript auszuführen, das sich auf einem lokalen linux/windowsComputer und auf Ihrem Remotecomputer befindet Windows, besteht es aus mehreren Zeilen mit **

plink root@MachineB -m local_script.bat

wird nicht funktionieren.

Es wird nur die erste Zeile des Skripts ausgeführt. Dies ist wahrscheinlich eine Einschränkung von plink.

Lösung 1:

So führen Sie ein mehrzeiliges Batch-Skript aus (insbesondere wenn es relativ einfach ist und aus wenigen Zeilen besteht):

Wenn Ihr ursprüngliches Batch-Skript wie folgt lautet

cd C:\Users\ipython_user\Desktop 
python filename.py

Sie können die Zeilen mithilfe des Trennzeichens "&&" wie folgt in Ihrer local_script.batDatei kombinieren : https://stackoverflow.com/a/8055390/4752883 :

cd C:\Users\ipython_user\Desktop && python filename.py

Nach dieser Änderung können Sie das Skript wie hier von @ JasonR.Coombs beschrieben ausführen: https://stackoverflow.com/a/2732991/4752883 mit:

`plink root@MachineB -m local_script.bat`

Lösung 2:

Wenn Ihr Batch-Skript relativ kompliziert ist, ist es möglicherweise besser, ein Batch-Skript zu verwenden, das den Befehl plink sowie die folgenden Anweisungen enthält : @Martin https://stackoverflow.com/a/32196999/4752883 :

rem Open tunnel in the background
start plink.exe -ssh [username]@[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N

rem Wait a second to let Plink establish the tunnel 
timeout /t 1

rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R

rem Kill the tunnel
taskkill /im plink.exe
alpha_989
quelle
1

Dieses Bash-Skript führt SSH auf einem Remote-Zielcomputer aus und führt einen Befehl auf dem Remotecomputer aus. Vergessen Sie nicht, Expect zu installieren, bevor Sie es ausführen (auf einem Mac brew install expect).

#!/usr/bin/expect
set username "enterusenamehere"
set password "enterpasswordhere"
set hosts "enteripaddressofhosthere"
spawn ssh  $username@$hosts
expect "$username@$hosts's password:"
send -- "$password\n"
expect "$"
send -- "somecommand on target remote machine here\n"
sleep 5
expect "$"
send -- "exit\n"
Mohammed Rafeeq
quelle
1
Dies ist großartig, wenn Sie Passwörter verwenden müssen. Für alle, die zu Hause zuschauen, sollte ein SSH-Befehl jedoch ein Paar öffentlicher + privater Schlüssel und keine Passwörter verwenden. Aktualisieren Sie Ihren SSH-Server, um die Passwörter vollständig auszuschalten
Scott Stensland
-1

Sie können runoverssh verwenden :

sudo apt install runoverssh
runoverssh -s localscript.sh user host1 host2 host3...

-s führt ein lokales Skript remote aus


Nützliche Flags:
-g Verwenden Sie ein globales Kennwort für alle Hosts (Eingabeaufforderung mit einem Kennwort).
-nVerwenden Sie SSH anstelle von sshpass, was für die Authentifizierung mit öffentlichem Schlüssel nützlich ist

Jetzt bei
quelle
-24

Kopieren Sie zunächst das Skript mit scp auf Maschine B.

[user @ machineA] $ scp / path / to / script user @ machineB: / home / user / path

Führen Sie dann einfach das Skript aus

[user @ machineA] $ ssh user @ machineB "/ home / user / path / script"

Dies funktioniert, wenn Sie dem Skript ausführbare Berechtigungen erteilt haben.

Baishampayan Ghose
quelle
Hallo, ich habe den empfohlenen Vorschlag angewendet, aber er gibt mir den folgenden Fehler [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: Name oder Dienst nicht bekannt [oracle @ node1 ~] $
'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh'? Falsche Befehlszeile, der Befehlsname sollte vom user @ host-Teil durch ein Leerzeichen und nicht durch einen Doppelpunkt getrennt werden.
Bortzmeyer
5
Ich stimme dem zu, weil die primäre Behauptung, dass es nicht ausgeführt werden kann, ohne es zu kopieren, falsch ist.
Jason R. Coombs
Es wäre hilfreich gewesen, wenn Jason hinzugefügt hätte, warum das falsch ist, anstatt nur die Tatsache zu erklären. Nicht hilfreich.
Kris
[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Oleksii Kyslytsyn