Sie schließen das externe Programm nicht, wenn Sie die STDOUT
close $out
schließen.
Um das Programm zu schließen, müssen Sie seine Prozess-ID erhalten und dann ein geeignetes Signal senden. Der open
Aufruf gibt die PID zurück, speichern Sie sie einfach. Dann sende ein Signal, wenn die Bedingung erfüllt ist.
use Scalar::Util qw(openhandle); # to check whether the handle is open
my $pid = open (my $out, "-|", "usfos $memory<input-$cpu.scr")
// die "Can't fork: $!"; #/stop editor red coloring
while (<$out>) {
if (/MIN STEP LENGTH/) {
kill "TERM", $pid; # or whatever is appropriate; add checks
close $out; # no check: no process any more so returns false
last;
}
}
# Close it -- if it is still open.
if (openhandle($out)) {
close $out or warn "Error closing pipe: $!";
}
Siehe open
und opentut
und close
.
Fortran des Fehler ist in der Tat über (STDOUT
) zu einer nicht vorhandenen Konsole zu schreiben, so scheint es mir, dass Sie das Problem richtig erkannt: als die Bedingung erfüllt ist, können Sie aufhören zu lesen (last
) und dann das Programm der schließen STDOUT
, die den gemeldeten Fehler beim nächsten Schreibversuch verursacht.
Ein paar Anmerkungen. Das Schließen des Rohrs wartet darauf, dass der andere Prozess beendet wird. Wenn das Programm am anderen Ende ein Problem hat, das unter close
bekannt ist, muss möglicherweise $?
abgefragt werden. Wenn die Pipe geschlossen wird, bevor das andere Programm fertig ist, wird es gesendet SIGPIPE
. Von close
Dokumentation
Wenn die Dateikennung von einem verrohrt öffnen, schließen kehrt kam falsch, wenn einer des anderen syscalls beteiligt ausfällt oder wenn sein Programm beendet mit nicht-Null-Status. Wenn das einzige Problem darin bestand, dass das Programm nicht von Null ging, $! wird auf 0 gesetzt. Das Schließen einer Pipe wartet auch darauf, dass der Prozess, der auf der Pipe ausgeführt wird, beendet wird - falls Sie später die Ausgabe der Pipe sehen wollen - und fügt implizit den Exit-Status-Wert dieses Befehls in $ ein. und $ {^ CHILD_ERROR_NATIVE}.
...
Schließen Sie das Leseende einer Pipe, bevor der Prozess am anderen Ende schreibt, um Ergebnisse in den Writer zu schreiben, der ein SIGPIPE empfängt. Wenn das andere Ende nicht damit umgehen kann, lesen Sie alle Daten, bevor Sie die Pipe schließen.
Anmerkung hinzugefügt Die Aufgabe ist, ein Verfahren zu töten, während seine STDOUT
mit einer Dateikennung verbunden ist und dass Dateihandle zu schließen (sobald eine Bedingung erfüllt ist). Wenn wir zuerst den Handle schließen, während der Prozess noch schreiben kann, senden wir einen SIGPIPE
an einen Prozess, über den wir vielleicht nicht viel wissen. (Geht es mit dem Signal um?) Auf der anderen Seite, wenn wir es zuerst beenden, kann die close($out)
nicht mit Erfolg abgeschlossen werden, da der Prozess, der $out
verbunden war, weg ist.Diese
Deshalb wird der Code nicht die Rückkehr überprüfen aus dem Aufruf von close
nachdem der Prozess abgebrochen wird, wie es ist falsch (wenn kill
gelungen). Nach der Schleife prüft es, ob der Griff noch offen ist, bevor er geschlossen wird, da wir ihn möglicherweise bereits geschlossen haben. Das Paket Scalar::Util
wird dafür verwendet, eine andere Option ist fileno
. Beachten Sie, dass der Code nicht prüft, ob kill
den Job ausgeführt hat, fügen Sie diesen nach Bedarf hinzu.
Auf einem Windows-System ist es ein wenig anders, da Sie die Prozess-ID des Kindes suchen müssen. Sie können dies unter Verwendung seines Namens tun und ihn dann entweder unter Verwendung von Win32::Process::Kill
oder unter Verwendung von kill
beenden. (Oder finden Sie einen Windows-Befehl, der all dies tun sollten, unten.) Um die ID-Prozess versuchen, entweder von
Mit Win32::Process::List
use Win32::Process::Kill;
use Win32::Process::List;
my $pobj = Win32::Process::List->new();
my %proc = $pobj->GetProcesses();
my $exitcode;
foreach my $pid (sort { $a <=> $b } keys %proc) {
my $name = $proc{$pid};
if ($name =~ /usfos\.exe/) {
Win32::Process::KillProcess($pid, \$exitcode);
# kill 21, $pid;
last;
}
}
Mit Win32::Process::Info
. Siehe this post.
use Win32::Process::Info;
Win32::Process::Info->Set(variant=>'WMI'); # SEE DOCS about WMI
my $pobj = Win32::Process::Info->new();
foreach my $pid ($pobj->ListPids) {
my ($info) = $pobj->GetProcInfo($pid);
if ($info->{CommandLine} =~ /^usfso/) { # command-line, not name
my $proc = $info->{ProcessId};
kill 2, $proc;
last;
}
}
Beachten Sie, dass dieses Modul stellt auch das Verfahren Subprocesses([$ppid,...])
, die alle Kinder der eingereichten $ppid
identifizieren (s). Es gibt einen Hash zurück, der von $ppid
s indiziert wird und ein Array-Ref mit $pid
s aller Unterprozesse enthält, für die jeweils $ppid
eingereicht wurde.
use Win32::Process::Info;
Win32::Process::Info->Set(variant=>'WMI');
my $pobj = Win32::Process::Info->new();
my %subproc = $pobj->Subprocesses([$pid]); # pid returned by open() call
my $rkids = $subproc{$pid};
foreach my $kid (@$rkids) {
print "pid: $kid\n"; # I'd first check what is there
}
Ich lief in einem Windows-Befehl TASKKILL /T
, die einen Prozess und seine Kinder
system("TASKKILL /F /T /PID $pid");
ich jetzt jede dieses Rechts beenden sollte nicht testen können.
Perl versucht nicht zu schreiben, das ist ein Rohr zu lesen, nicht zu schreiben. Es scheint, als wäre dein externes Programm derjenige, der den Fehler gibt. –