ssh + here document - Kommt Ctrl + C auf die Remote-Seite?

834
brunoais

Als Teil einer Reihe von Aufgaben, die ich erledigen muss, muss ich ein Bash-Skript erstellen, das auf einen Remotecomputer zugreift, drei Befehle ausführt, darauf wartet, dass ein Prozess durch ein Ende beendet wird SIGINT(oder einfach nur beendet wird), und danach eine Bereinigung durchführen.

Derzeit verwende ich diesen Code:

#! /bin/bash  # local preparations # ...  ssh -t $USER@remote.far <<-'COMMANDS'  echo "Preparing execution"  java -jar execute.jar & executePID=$!  echo "Ready." echo "CTRL+C to clean and close"  trap "kill $executePID" INT HUP wait $executePID  #cleanup code here  echo "done. Logging out" sleep 2 logout COMMANDS  # Final local cleanup 

Dieser Code scheint nur zu funktionieren, bis der waitBefehl (builtin) gefunden wird. waitscheint alle Befehle zu verbrauchen, die danach kommen. Wenn ich versuche, ein SIGINT( Ctrl+ C) zu senden, scheint es nicht, den Trap-Inhalt und den gesamten Bereinigungscode auszuführen.

Wie kann ich das beheben, damit es wie erwartet funktioniert?

Ich darf diese Bash-Datei nicht in mehrere Dateien aufteilen und auch keine Skripts auf dem Remote-Computer erstellen, auch wenn sie nur temporär sind.
Beide Computer verwenden dieselbe Linux-Variante.

0
Was ist, wenn Sie die `wait $ executePID` außerhalb des Befehls ssh verschieben? Sie könnten dann $ executePID anstelle des Java-Befehls durch die PID des Befehls ssh ersetzen. Wollen Sie damit sagen, dass Sie, wenn Sie dieses Skript & ctrl-c ausführen, einige zusätzliche Befehle zur Bereinigung des Remote-Hosts ausführen möchten? Wenn ja, was müssen Sie beim Erstellen Ihrer eigenen Trap-Handhabungsfunktion mit `trap ctrl_c INT` beachten und dann eine Funktion namens ctrl_c schreiben, die aufgerufen wird, wenn ein SIGINT ausgelöst wird [siehe diesen Blogbeitrag] (https: // rimuhosting .com / knowledgebase / linux / misc / trapping-ctrl-c-in-bash). stuts vor 7 Jahren 0
Wissen Sie, dass das Skript nach meinen vorherigen Kommentaren definitiv beim Warten aufhängt? Ihr Java-Befehl startet den Befehl nicht im Hintergrund (durch Beenden der Zeile mit `&`). Möglicherweise wird Ihr Skript den Java-Prozess starten. Dort trifft die Strg-C den Befehl. Sehen Sie definitiv die 'Ready' & 'CTRL-C'-Echos, wenn Sie Ihr Skript ausführen? stuts vor 7 Jahren 0
@stuts Entschuldigung für die Verzögerung. Ich weiß, wo es hängt, weil ich einige Debug-Ausgaben für die Bash-Konsole und einige Debug-Dateien für das Java-Programm erstellt habe. Das CTRL- Cwird an Java gesendet und das Java-Programm wird erwartungsgemäß beendet. Sie haben einen Punkt über das fehlende "&" in diesem Beispielskript. Ich werde es reparieren. brunoais vor 7 Jahren 0

1 Antwort auf die Frage

1
Kamil Maciorowski

Erläuterung

Es geht nicht darum wait. Ich denke, das ist was passiert:

Sie verwenden <<, so stdinvon sshbis zu einem gewissen Descriptor umgeleitet wird, durch die das gesamte hier Dokument fließt.

Diese andere Antwort erläutert, wie + ssherfasst werden kann, wenn verwendet wird. Darauf kommt es an:CtrlC-t

Auf der Clientseite sshwird versucht, den ttyvon stdin" used " in den "raw" -Modus zu setzen. […] Das Einstellen des Raw-Modus bedeutet, dass Zeichen, die normalerweise Signale (wie z. B. Ctrl+ C) senden würden, stattdessen einfach in den Eingabestrom eingefügt werden.

In Ihrem Fall ist es nicht dasselbe, das stdinIhre lokale Shell verwendet. ttyDie von Ihrer lokalen Shell verwendete Shell bleibt erhalten, sie ist niemals auf den "rohen" Modus eingestellt.

Wenn Sie also Ctrl+ Cdrücken, wirkt es lokal und wird beendet ssh. In diesem Moment bekommt die entfernte Seite SIGHUP. Deine trapArbeiten und tötet java. Ich denke, es gibt hier eine Falle: a trapführt einen bestimmten Code als Antwort auf ein gegebenes Signal aus, verhindert jedoch nicht, dass das Signal seine normale Wirkung hat. Daher scheint es mir, dass Sie javaauch ohne einen getötet werden würden, trapweil es eine Aufgabe der Shell ist, die als Antwort beendet wird SIGHUP.

Die Shell, die beendet wird, hört auf, ihre Shell zu lesen und zu interpretieren stdin. Deshalb wird alles, was folgt, waitverworfen.


Lösung

commands() { cat <<-'COMMANDS'  cleanup() { # cleanup code here echo "Done. Logging out" sleep 2 logout }  echo "Preparing execution"  java -jar execute.jar & executePID=$!  echo "Ready." echo "CTRL+C to clean and close"  trap "kill $executePID; cleanup" INT HUP wait $executePID  cleanup COMMANDS  }  stty raw -echo; cat <(commands) - | ssh -t $USER@remote.far; stty -raw echo 

Die letzte Zeile ist der eigentliche Befehl. Zuerst bereiten wir ttyso Ctrl+ Cnicht lokal handeln kann. Dann verketten wir Befehle und Standardeingaben und übergeben sie an die Remote-Shell. Ich kann dies mit diesem Dokument nicht direkt tun, die commandFunktion ist eine Problemumgehung (es wäre einfacher, eine reguläre Datei zu verwenden, aber Sie sagten, Sie könnten nicht mehr als eine verwenden). Nach sshund catBeenden setzen wir den ttyNormalzustand.

Ein Pseudo-Terminal ist unerlässlich, stellen Sie also sicher, dass es ssh -tfunktioniert ( -ttggf. verwenden).

Die Remote-Shell muss alle Befehle lesen (zwischenspeichern) commands, nur dann kann Ctrl+ sie erhalten, Cwenn Sie sie drücken. Ich denke, das bedeutet, dass Sie danach nicht zu viel Code haben können wait. Was auch immer Sie danach tun möchten, SIGINTmuss von innen ausgeführt werden trap. Dies ist der Grund, warum ich eine einzige cleanupFunktion verwendet habe, die alles macht.

Der Code ist nicht narrensicher. Drücken Sie Ctrl+ Czu früh oder mehrmals und Sie befinden sich in der Remote-Shell oder mit etwas "defektem" Local tty. In diesem Fall setzen Sie den Befehl resetmit dem Befehl zurück tty.

Sie müssen eine Taste drücken (z. B. Enter), wenn "Verbindung zu ... geschlossen" angezeigt wird. Der Grund ist cat, dass die Pipe sshnicht mehr kaputt ist (weil sie nicht mehr vorhanden ist), bis versucht wird, etwas zu schreiben.


Alternative

Wenn die oben genannte Lösung aus irgendeinem Grund nicht für Sie funktioniert, verwenden Sie diese einfachere Alternative. Das hier Dokument ist genau wie zuvor. In diesem Fall machen wir nicht mit Zohan an tty, Ctrl+ Cbeenden lokale sshwie es mit Ihrem ursprünglichen Code tut. Der Unterschied (in Bezug auf Ihren Code) ist trapdie Reinigung. Ich denke, es wäre genug, SIGHUPnur eine Falle zu stellen .

ssh -t $USER@remote.far <<-'COMMANDS'  cleanup() { # cleanup code here echo "Done. Logging out" sleep 2 logout }  echo "Preparing execution"  java -jar execute.jar & executePID=$!  echo "Ready." echo "CTRL+C to clean and close"  trap "kill $executePID; cleanup" INT HUP wait $executePID  cleanup COMMANDS 

Hinweis: Wenn der trapAuslöser ausgelöst wird, wird cleanupeine Nachricht angezeigt, die jedoch nicht angezeigt wird, da Ihre lokale Verbindung sshbereits getrennt ist. Sie werden die Nachricht nur sehen, wenn Sie javaohne das Symbol beenden trap. Ihr Bereinigungscode ( # cleanup code here) sollte noch ausgeführt werden.

Sieht großartig aus! Vielen Dank! Ich werde es versuchen, wenn ich kann, und dann werde ich versuchen, darüber zu berichten. brunoais vor 7 Jahren 0