Short Verwenden fork + exec (empfohlen) haben, oder die Nutzung offen verrohrt oder -t
Pseudo-Terminal fallen
Hier ist viel los. Sie starten ssh
, die sich mit (Pseudo-) Terminal-Geschäft befassen müssen, mit Streams, die manipuliert werden - und für einen Hintergrundprozess. Es ist dieses letzte Stück, das es schwierig macht. Ich bekomme das gleiche Verhalten wie du.
Hier sind drei Lösungen/Workarounds, die für mich arbeiten. Ich empfehle das erste.
Der Punkt der &
ist, dass Ihr Skript die Kontrolle sofort zurück erhält. Das können wir im Skript gut erreichen. Das ist viel sauberer und hängt nicht von Systemdetails ab. Lassen Sie also &
fallen und stellen Sie den (ssh) Prozess selbst in den Hintergrund.
use warnings;
use strict;
my $cmd = 'ssh -t machine command >stdout.log 2>error.log';
my $pid = fork;
die "Can't fork: $!" if not defined $pid;
if ($pid == 0) { # child
exec($cmd);
die "exec shouldn't have returned: $! ";
}
# Parent only. Child became $cmd, which went its own way, never to return.
# Rest of your code, executed right away ...
Die error.log
enthält wahrscheinlich die Linie Connection to machine closed.
(es für mich tut). Eine grundlegende Erklärung von fork + exec ist am Ende, falls erforderlich.
Nachfolgend sind Abhilfen, die Probleme und können nicht überall arbeiten kann, aber sie erreichen, was in meinen Tests benötigt wird.
Wenn command
braucht nicht die Steuerung tty Tropfen -t
. Die Umleitung und &
funktionieren immer noch für mich.
Trick das STDIN
Problem, indem den (SSH) Prozess beginnend Verwendung verrohrt offene
my $cmd = 'ssh -t machine command >stdout.log 2>error.log &';
my $pid = open my $fh, '|-', $cmd // die "Can't fork: $!;
# your other code ...
close $fh;
Diese 'öffnet' (Gabeln) ein Verfahren, das $cmd
so ausführt, dass $fh
sein ist STDIN
und unser Skript kann es bei Bedarf einspeisen. So bieten wir die STDIN
und ssh Pseudo-Terminal hat kein Problem damit. Wenn der Befehl keine Eingabe benötigt, können Sie dies ignorieren und der Rest funktioniert nach Bedarf.
Dies ist eine sehr saubere Art und Weise ein weiteres Verfahren und Rohr gabeln/oder von seinem STDIN
STDOUT
siehe tutorial perlopentut
Bezug open
, und für weitere Einzelheiten Safe Pipe Opens in perlipc
. In diesem Fall würde ich jedoch lieber mit fork + exec oben gehen.
Die fork
erstellt einen neuen Prozess und gibt deren ID. Dieser neue Prozess ist ein Klon des übergeordneten Elements, außer dass für diesen Prozess $pid
Null ist, während es für das übergeordnete Element eine (große) Ganzzahl ist. (Es gibt einige andere, wesentlich kleinere Unterschiede.) Also gibt das Kind den nächsten if ($pid == 0) { }
Block ein, während der Elternteil das nicht tut. Auf diese Weise trennen wir Kind und Eltern, sodass jeder den ihm zugewiesenen Code ausführen kann. Wir erhalten parallele Ausführung (im Prinzip und auch meistens in der Praxis).
Im Innern des if
Block, der durch das Kind Prozess ausgeführt Programm wird von exec
, in diesem Fall von Ihrem ssh
Befehl insgesamt durch das Programm ausgeführt ersetzt. Die exec
wird einfach nie zurückgegeben (außer der Anruf selbst schlägt fehl). Das Parent läuft nach dem if
weiter und führt normalerweise jeden Code aus, der ohne Blockierung vorhanden ist. Auf diese Weise können wir ein anderes Programm ausgliedern, während Ihr Programm mit seinem Geschäft fortfahren kann, ohne auf den Abschluss des anderen warten zu müssen.
Das sieht nicht wie ein Fehler aus. – melpomene
Haben Sie versucht, Ihren Befehl in einem normalen Shell-Skript auszuführen? Ich denke nicht, dass es ein Perl-Problem ist, sondern ein SSH-Problem. – dgw