
Ich habe ein Bash-Skript, das ein Python3-Skript (nennen wir es startup.sh
) mit der folgenden Schlüsselzeile startet:
nohup python3 -u <script> &
Wenn ich ssh
direkt einsteige und dieses Skript aufrufe, läuft das Python-Skript nach dem Beenden im Hintergrund weiter. Wenn ich jedoch Folgendes ausführe:
ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"
Der Prozess wird beendet, sobald ssh
er vollständig ausgeführt wurde und die Sitzung geschlossen wird.
Was ist der Unterschied zwischen den beiden?
BEARBEITEN: Das Python-Skript führt einen Webdienst über Bottle aus.
EDIT2: Ich habe auch versuchtErstellen eines Init-Skriptsdas ruft auf startup.sh
und läuft ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>"
, aber es tritt das gleiche Verhalten auf.
EDIT3: Vielleicht liegt es an etwas anderem im Skript. Hier ist der Hauptteil des Skripts:
chmod 700 ${key_loc}
echo "INFO: Syncing files."
rsync -azP -e "ssh -i ${key_loc} -o StrictHostKeyChecking=no" ${source_client_loc} ${remote_user}@${remote_hostname}:${destination_client_loc}
echo "INFO: Running startup script."
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart"
EDIT4: Wenn ich die letzte Zeile mit einem Sleep am Ende ausführe:
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart; sleep 1"
echo "Finished"
Es erreicht nie echo "Finished"
und ich sehe die Bottle-Server-Nachricht, die ich noch nie zuvor gesehen habe:
Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.
Ich sehe „Fertig“, wenn ich mich manuell per SSH anmelde und den Prozess selbst beende.
EDIT5: Wenn ich mit EDIT4 eine Anfrage an einen beliebigen Endpunkt stelle, erhalte ich eine Seite zurück, aber die Flasche gibt einen Fehler aus:
Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.
----------------------------------------
Exception happened during processing of request from ('<IP>', 55104)
Antwort1
Ich würde den Befehl von seinen Standard-Eingabe-/Ausgabe- und Fehlerflüssen trennen:
nohup python3 -u <script> </dev/null >/dev/null 2>&1 &
ssh
braucht einen Indikator, der keine weitere Ausgabe hat und keine weitere Eingabe erfordert. Wenn die Eingabe etwas anderes ist und die Ausgabe umgeleitet wird, ssh
kann das Programm sicher beendet werden, da die Eingabe/Ausgabe nicht vom Terminal kommt oder dorthin geht. Das bedeutet, dass die Eingabe von woanders kommen muss und die Ausgabe (sowohl STDOUT als auch STDERR) woanders hingehen sollte.
Der </dev/null
Teil gibt /dev/null
als Eingabe für an <script>
. Warum das hier sinnvoll ist:
Die Umleitung von /dev/null auf stdin führt zu einem sofortigen EOF für jeden Leseaufruf dieses Prozesses. Dies ist normalerweise nützlich, um einen Prozess von einem TTY zu trennen (ein solcher Prozess wird als Daemon bezeichnet). Wenn Sie beispielsweise einen Hintergrundprozess remote über SSH starten, müssen Sie stdin umleiten, um zu verhindern, dass der Prozess auf lokale Eingaben wartet. https://stackoverflow.com/questions/19955260/what-is-dev-null-in-bash/19955475#19955475
Alternativ sollte die Umleitung von einer anderen Eingabequelle relativ sicher sein, solange die aktuelle ssh
Sitzung nicht geöffnet bleiben muss.
Mit diesem >/dev/null
Teil leitet die Shell die Standardausgabe nach /dev/null um und verwirft sie im Wesentlichen. >/path/to/file
wird auch funktionieren.
Der letzte Teil 2>&1
ist die Umleitung von STDERR zu STDOUT.
Es gibt drei Standardquellen für die Eingabe und Ausgabe eines Programms. Die Standardeingabe erfolgt normalerweise über die Tastatur, wenn es sich um ein interaktives Programm handelt, oder über ein anderes Programm, wenn es die Ausgabe des anderen Programms verarbeitet. Das Programm druckt normalerweise auf die Standardausgabe und manchmal auf die Standardfehlerausgabe. Diese drei Dateideskriptoren (Sie können sie sich als „Datenleitungen“ vorstellen) werden oft als STDIN, STDOUT und STDERR bezeichnet.
Manchmal werden sie nicht benannt, sondern nummeriert! Die integrierten Nummerierungen für sie sind 0, 1 und 2, in dieser Reihenfolge. Wenn Sie nicht explizit einen Namen oder eine Nummer angeben, sprechen Sie standardmäßig von STDOUT.
In diesem Kontext können Sie sehen, dass der obige Befehl die Standardausgabe nach /dev/null umleitet. Dies ist ein Ort, an den Sie alles werfen können, was Sie nicht möchten (oft als Bit-Bucket bezeichnet). Anschließend leitet er die Standardfehlerausgabe in die Standardausgabe um (dabei müssen Sie vor dem Ziel ein & setzen).
Die kurze Erklärung lautet daher: „Die gesamte Ausgabe dieses Befehls sollte in ein schwarzes Loch geschoben werden.“ Das ist eine gute Möglichkeit, ein Programm wirklich leise zu machen!
Was bedeutet > /dev/null 2>&1? | Xaprb
Antwort2
Ansehen man ssh
:
ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname [command]
Beim Ausführen ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"
führen Sie das Shell-Skript startup.sh als SSH-Befehl aus.
Aus der Beschreibung:
Wenn ein Befehl angegeben ist, wird er auf dem Remote-Host statt in einer Login-Shell ausgeführt.
Auf dieser Grundlage sollte das Skript remote ausgeführt werden.
Der Unterschied zwischen dieser Ausführung und der Ausführung nohup python3 -u <script> &
in Ihrem lokalen Terminal besteht darin, dass dies als lokaler Hintergrundprozess ausgeführt wird, während der SSH-Befehl versucht, es als Remote-Hintergrundprozess auszuführen.
Wenn Sie das Skript lokal ausführen möchten, führen Sie startup.sh nicht als Teil des SSH-Befehls aus. Sie könnten etwas wie Folgendes versuchen:ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"
Wenn Sie das Skript remote ausführen möchten und dieser Vorgang nach Beendigung Ihrer SSH-Sitzung fortgesetzt werden soll, müssen Sie zunächst eine screen
Sitzung auf dem Remote-Host starten. Anschließend müssen Sie das Python-Skript auf dem Bildschirm ausführen. Es wird nach Beendigung Ihrer SSH-Sitzung weiter ausgeführt.
SehenBildschirm-Benutzerhandbuch
Ich denke zwar, dass screen die beste Option für Sie ist, aber wenn Sie nohup verwenden müssen, sollten Sie shopt -s huponexit
vor dem Ausführen des nohup-Befehls die Einstellungen auf dem Remote-Host vornehmen. Alternativ können Sie disown -h [jobID]
den Prozess markieren, damit kein SIGHUP an ihn gesendet wird.1
Wie kann ich einen Job weiter ausführen, nachdem ich eine Shell-Eingabeaufforderung im Hintergrund beendet habe?
Das SIGHUP-Signal (Auflegen) wird von Ihrem System beim Steuern des Terminals oder beim Beenden des Steuerprozesses verwendet. Sie können SIGHUP auch verwenden, um Konfigurationsdateien neu zu laden und Protokolldateien zu öffnen/schließen. Mit anderen Worten: Wenn Sie sich von Ihrem Terminal abmelden, werden alle laufenden Jobs beendet. Um dies zu vermeiden, können Sie die Option -h an den Befehl disown übergeben. Diese Option markiert jede Job-ID, sodass SIGHUP nicht an den Job gesendet wird, wenn die Shell ein SIGHUP empfängt.
Lesen Sie auch diese Zusammenfassung, wie es huponexit
funktioniert, wenn eine Shell beendet, beendet oder gelöscht wird. Ich vermute, Ihr aktuelles Problem hängt damit zusammen, wie die Shell-Sitzung beendet wird.2
Alle untergeordneten Prozesse (ob im Hintergrund oder nicht) einer über eine SSH-Verbindung geöffneten Shell werden beim Schließen der SSH-Verbindung nur dann mit SIGHUP beendet, wenn die Option „huponexit“ gesetzt ist: Führen Sie „shopt huponexit“ aus, um zu prüfen, ob dies zutrifft.
Wenn huponexit wahr ist, können Sie nohup oder disown verwenden, um den Prozess von der Shell zu trennen, damit er beim Beenden nicht beendet wird. Oder führen Sie die Dinge mit screen aus.
Wenn „huponexit“ auf „false“ gesetzt ist, was heutzutage zumindest bei einigen Linux-Betriebssystemen die Standardeinstellung ist, werden im Hintergrund laufende Jobs bei einer normalen Abmeldung nicht beendet.
- Aber selbst wenn huponexit falsch ist, werden im Hintergrund laufende Prozesse trotzdem beendet, wenn die SSH-Verbindung beendet wird oder abbricht (anders als beim normalen Logout). Dies kann durch disown oder nohup wie in (2) vermieden werden.
Abschließend finden Sie hier einige Beispiele zur Verwendung von shopt huponexit.3
$ shopt -s huponexit; shopt | grep huponexit
huponexit on
# Background jobs will be terminated with SIGHUP when shell exits
$ shopt -u huponexit; shopt | grep huponexit
huponexit off
# Background jobs will NOT be terminated with SIGHUP when shell exits
Antwort3
Vielleicht lohnt es sich, -n
diese Option beim Starten eines auszuprobieren ssh
? Dadurch wird die Abhängigkeit eines Remote-Prozesses von einem lokalen verhindert stdin
, der natürlich geschlossen wird, sobald ssh session
er endet. Und dies führt dazu, dass der Remote-Prozess beendet wird, wenn er versucht, auf seinen zuzugreifen stdin
.
Antwort4
Das klingt eher nach einem Problem mit dem, was das python
Skript oder python
es selbst tut. Alles, was nohup
es wirklich tut (abgesehen von der Vereinfachung von Weiterleitungen), ist, den Handler für das HUP
Signal SIG_IGN
vor dem Ausführen des Programms auf (Ignorieren) zu setzen. Es gibt nichts, was das Programm daran hindern könnte, es zurückzusetzen SIG_DFL
oder seinen eigenen Handler zu installieren, sobald es ausgeführt wird.
Sie können beispielsweise versuchen, Ihren Befehl in Klammern einzuschließen, damit Sie einen doppelten Fork-Effekt erzielen und Ihr python
Skript nicht länger ein untergeordnetes Element des Shell-Prozesses ist. Beispiel:
( nohup python3 -u <script> & )
Eine andere Möglichkeit, die ebenfalls einen Versuch wert sein könnte (wenn Sie bash
und keine andere Shell verwenden), ist die Verwendung des disown
integrierten anstelle von nohup
. Wenn alles wie dokumentiert funktioniert, sollte dies eigentlich keinen Unterschied machen, aber in einer interaktiven Shell würde dies die HUP
Signalweiterleitung zu Ihrem python
Skript verhindern. Sie können das Disown in der nächsten Zeile oder in derselben wie unten hinzufügen (beachten Sie, dass das Hinzufügen von a ;
nach a &
ein Fehler in ist bash
):
python3 -u <script> </dev/null &>/dev/null & disown
Wenn das oben genannte oder eine Kombination davon nicht funktioniert, dann ist die einzige Möglichkeit, das Problem zu beheben, sicherlich das python
Skript selbst.