2010-07-13 6 views
11

Ich möchte ein paar Jobs auf verschiedenen Maschinen mit ssh starten. Wenn der Benutzer dann das Hauptscript unterbricht, möchte ich alle Jobs ordnungsgemäß herunterfahren.Starten eines Prozesses über SSH mit bash und dann töten es auf sigint

Hier ist ein kurzes Beispiel dafür, was ich versuche zu tun:

#!/bin/bash 
trap "aborted" SIGINT SIGTERM 
aborted() { 
    kill -SIGTERM $bash2_pid 
    exit 
} 

ssh -t remote_machine /foo/bar.sh & 
bash2_pid=$! 
wait 

jedoch der bar.sh Prozess noch den Remote-Computer ausgeführt. Wenn ich dieselben Befehle in einem Terminalfenster mache, wird der Prozess auf dem entfernten Host beendet.

Gibt es eine einfache Möglichkeit, dies zu erreichen, wenn ich das Bash-Skript ausführen? Oder muss ich mich anmelden, um den Remote-Computer zu finden, den richtigen Prozess zu finden und ihn so zu töten?

edit: Scheint, wie ich mit der Option B gehen, die Tötung der remotescript durch eine andere SSH-Verbindung

Also nein, ich möchte wissen, wie ich die remotepid bekommen? Ich habe ein etwas entlang der Linien von versucht:

remote_pid=$(ssh remote_machine '{ /foo/bar.sh & } ; echo $!') 

Dies gilt nicht, da es blockiert arbeiten.

Wie warte ich auf eine Variable zum Drucken und dann "Freigabe" eines Unterprozesses?

+1

Haben Sie in Ihrem Remote-Skript eine Falle versucht? Vielleicht SIGHUP? –

+0

Ich habe versucht, alle Signale zu fangen, die ich ohne Erfolg denken kann ... Ich denke, ich muss nur noch auf die Remote-Maschine ssh und führen Sie Kill auf das Skript, aber dann brauche ich die PID von der remote Prozess ... – getekha

+0

Probier 'pkill' wenn das so ist, wirst du gehen. Roh, aber normalerweise effektiv. – bstpierre

Antwort

16

Es wäre auf jeden Fall vorzuziehen, Ihre Bereinigung von der ssh verwaltet zu bekommen, die den Prozess startet, anstatt sich später mit einer zweiten ssh-Sitzung auf den Kill zu begeben.

Wenn ssh an Ihr Terminal angeschlossen ist; es verhält sich ziemlich gut. Trennen Sie es jedoch von Ihrem Terminal und es wird (wie Sie bemerkt haben) ein Schmerz, um entfernte Prozesse zu signalisieren oder zu verwalten. Sie können die Verbindung herunterfahren, nicht jedoch die Remote-Prozesse.

Das lässt Sie mit einer Option: Verwenden Sie den Link als eine Möglichkeit für den Remote-Prozess, um benachrichtigt werden, dass es heruntergefahren werden muss. Der sauberste Weg dazu ist die Verwendung blockierender E/A. Machen Sie die Remote-Leseeingabe von ssh und wann der Prozess heruntergefahren werden soll; Bitte senden Sie es einige Daten, so dass der Lesevorgang des Remote-deblockiert und es kann mit der Reinigung fortfahren:

command & read; kill $! 

Dies ist, was wir auf der Fernbedienung ausführen möchte. Wir rufen unseren Befehl auf, den wir remote ausführen möchten; Wir lesen eine Textzeile (Blöcke, bis wir eine erhalten) und wenn wir fertig sind, signalisieren wir den Befehl zu beenden.

Um das Signal von unserem lokalen Skript an die Fernbedienung zu senden, müssen wir nur eine Textzeile senden. Leider bietet Bash hier nicht viele gute Möglichkeiten. Zumindest nicht, wenn Sie mit bash < 4.0 kompatibel sein wollen.

Mit bash 4 wir Co-Prozesse verwenden können:

coproc ssh [email protected] 'command & read; kill $!' 
trap 'echo >&"${COPROC[1]}"' EXIT 
... 

Nun, wenn das lokale Skript beendet (nicht Falle auf INT, TERM, etc. Nur EXIT) sendet er eine neue Zeile der Datei im zweiten Element des Arrays COPROC. Diese Datei ist eine Pipe, die mit ssh 's stdin verbunden ist und unsere Leitung effektiv zu ssh leitet. Der Fernbefehl liest die Leitung, beendet den Befehl read und kill.

Vor bash 4 Dinge werden ein bisschen schwieriger, da wir keine Co-Prozesse haben. In diesem Fall müssen wir uns die Rohrleitung tun:

mkfifo /tmp/mysshcommand 
ssh [email protected] 'command & read; kill $!' < /tmp/mysshcommand & 
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT 

Dies sollte so ziemlich jede bash-Version arbeiten.

+0

Dies sollte funktionieren. Das Skript muss auf Bash 3.2 laufen, also muss ich meine eigenen Pipes machen. Der normale Fall ist, dass der Remote-Prozess normal endet, so dass ich mich nicht auf die Falle verlassen kann, um den Fifo zu bereinigen. (Es könnte sein, wenn ich sie alle in einer Liste hätte und das Aufräumen am Ende des Programms gemacht hätte, aber ich würde sie lieber entfernen, sobald ich mit ihnen fertig bin). – getekha

+0

Das funktioniert, aber das Problem kommt jetzt, wenn das Programm normal endet: Der Leseaufruf benötigt noch eine Eingabe zum Beenden. Irgendwelche Ideen, wie man beide Fälle deckt? – jkp

+0

@jkp: EXIT löst aus, wenn das Programm auch normal beendet wird. Es sollte beide Fälle abdecken. – lhunath

-1

Sie sollten in Erwägung ziehen, das Remote-Dateisystem zu mounten und das Skript von der Master-Box aus auszuführen. Zum Beispiel, wenn Ihr Kernel mit Sicherung kompiliert wird (kann mit folgendem überprüfen):

/sbin/lsmod | grep -i fuse 

können Sie dann montieren Sie das Remote-Dateisystem mit dem folgenden Befehl:

sshfs [email protected]_system: mount_point 

Jetzt führen Sie einfach Ihr Skript auf die Datei in mount_point.

+0

Der Zweck des echten Skripts ist es, die Arbeit auf eine Handvoll Slave-Maschinen zu verteilen, also würde das nicht funktionieren Ich .. – getekha

6

Versuchen Sie folgendes:

ssh -tt host command </dev/null & 

Wenn Sie den lokalen SSH-Prozess zu beenden, wird der Remote-pty schließen und SIGHUP wird an den Remote-Prozess gesendet werden.

+0

Dies ist nicht ideal für Scripting, insbesondere wenn das Skript an ssh übergeben wird, da es die Ausgabe zeilenweise so ausgibt, als wäre es ein interaktives Terminal. –

0

Die Lösung für Bash 3.2:

mkfifo /tmp/mysshcommand 
ssh [email protected] 'command & read; kill $!' < /tmp/mysshcommand & 
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT 

funktioniert nicht. Der ssh-Befehl befindet sich nicht in der ps-Liste auf dem Client-Computer. Erst wenn ich etwas in die Pipe zurückgebe, erscheint es in der Prozessliste des Client-Rechners. Der Prozess, der auf der "Server" -Maschine erscheint, wäre nur der Befehl selbst, nicht der Lese/Kill-Teil.

Das erneute Schreiben in die Pipe beendet den Prozess nicht.

Also zusammenfassend, ich muss in die Pipe für den Befehl zu starten, zu schreiben, und wenn ich wieder schreibe, tötet es nicht den Remote-Befehl, wie erwartet.

1

Referenzierung die Antwort von lhunath und https://unix.stackexchange.com/questions/71205/background-process-pipe-input kam ich mit diesem Skript bis

run.sh:

#/bin/bash 
log="log"                     
eval "[email protected]" \&                    
PID=$!                      
echo "running" "[email protected]" "in PID $PID"> $log             
{ (cat <&3 3<&- >/dev/null; kill $PID; echo "killed" >> $log) & } 3<&0        
trap "echo EXIT >> $log" EXIT                
wait $PID 

Der Unterschied ist, dass diese Version den Prozess tötet, wenn die Verbindung geschlossen wird, sondern auch Erträge Der Beendigungscode des Befehls, wenn er vollständig ausgeführt wird.

$ ssh localhost ./run.sh true; echo $?; cat log 
0 
running true in PID 19247 
EXIT 

$ ssh localhost ./run.sh false; echo $?; cat log 
1 
running false in PID 19298 
EXIT 

$ ssh localhost ./run.sh sleep 99; echo $?; cat log 
^C130 
running sleep 99 in PID 20499 
killed 
EXIT 

$ ssh localhost ./run.sh sleep 2; echo $?; cat log 
0 
running sleep 2 in PID 20556 
EXIT 

Für einen Einzeiler:

ssh localhost "sleep 99 & PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID" 

Für Komfort:

HUP_KILL="& PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID" 
ssh localhost "sleep 99 $HUP_KILL" 

Hinweis: töten 0 bevorzugt sein kann, $ PID zu töten in Abhängigkeit vom Verhalten in Bezug benötigt gelaicht Kindprozesse. Sie können auch -HUP oder töten -INT töten, wenn Sie es wünschen.

Update: Ein sekundärer Jobkontrollkanal ist besser als das Lesen von stdin.

ssh -n -R9002:localhost:8001 -L8001:localhost:9001 localhost ./test.sh sleep 2 

Set Job-Steuerungsmodus und überwachen den Auftragssteuerkanal:

set -m 
trap "kill %1 %2 %3" EXIT 
(sleep infinity | netcat -l 127.0.0.1 9001) & 
(netcat -d 127.0.0.1 9002; kill -INT $$) & 
"[email protected]" & 
wait %3 

Schließlich, hier ist ein weiterer Ansatz, und ein Verweis auf einen auf openssh eingereicht Fehler: https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14

Dies ist die beste So habe ich es gefunden. Sie möchten etwas auf der Serverseite, das versucht, stdin zu lesen, und dann die Prozessgruppe abbricht, wenn dies fehlschlägt, aber Sie möchten auch eine stdin auf der Client-Seite, die blockiert, bis der serverseitige Prozess abgeschlossen ist und veraltete Prozesse wie < (Schlaf-Unendlichkeit) könnte.

Es scheint tatsächlich nirgends stdout umadressieren, aber es funktioniert als eine blockierende Eingabe und vermeidet die Erfassung von Tastenanschlägen.

Verwandte Themen