2010-12-07 7 views
1

Ich schreibe ein Perl-Skript, das ein externes Skript verwendet. Das externe Skript muss aus einem bestimmten Verzeichnis laufen, so fand ich die folgende nützlich:Wie kann ich einen Systembefehl ausführen und sterben, wenn etwas in STDERR geschrieben wird?

use IPC::System::Simple qw(capture); 

my @args = ('external script path...', 'arg1', ...); 
my $out = capture([0], "cd $dir ; @args"); 

Manchmal ist die externe Skript Sachen zu STDERR schreibt aber immer noch 0 zurück Ich möchte diese Zeiten erfassen und confess (oder die). Da ich nicht den Rückgabewert des externen Skript steuern, dachte ich, vielleicht könnte ich seine STDERR erfassen, so werde ich etwas wie diese:

my ($out, $err) = cool_capture([0], "cd $dir ; @args"); 
say "Output was: $out"; 
if ($err) { 
die "Error: this was written to STDERR: $err"; 
} 

Was kann ich tun?

Antwort

6

Dies ist covered in der Perl-FAQ.

test_app Vorausgesetzt ist ein Programm, das eine Zeile auf stdout und eine Zeile zu stderr ausgibt:

use IPC::Open3; 
use Symbol 'gensym'; 

my($wtr, $rdr, $err); 
$err = gensym; 

my $pid = open3($wtr, $rdr, $err, 'test_app'); 

waitpid($pid, 0); 
my $status = $? >> 8; 

my $stdout = <$rdr>; 
my $stderr = <$err>; 

print "out output: $stdout\n"; 
print "err output: $stderr\n"; 

print "Exit code: $status\n"; 

EDIT: Per Antrag den Exit-Code Erfassung aktualisiert und enthält. Sie könnten auch perldoc IPC::Open3 gefragt, die sagt

waitpid($pid, 0); 
my $child_exit_status = $? >> 8; 

Und das sollten Sie auf jeden Fall für seine Vorsichtsmaßnahmen und Einschränkungen lesen.

+0

+1 für die Angabe der Referenz im Perl Doc und die Bereitstellung der Antwort. –

+0

Kopieren/Einfügen von Dokumentation für Spaß und Profit ... – Sorpigal

+0

+1 Danke. Können Sie dies erweitern, um zu überprüfen, ob der Rückgabewert von 'test_app' gültig ist (z. B.' 0', wie in 'capture ([0], ...)')? –

1

Wenn eine signifikante Ausgabe in stdout und/oder stderr geschrieben wird oder Sie sowohl lesen als auch in den Prozess schreiben. Sie müssen viel vorsichtiger mit Ihrer E/A-Handhabung umgehen, um verschiedene Blockierungsprobleme zu vermeiden.

my ($wtr, $rdr, $err) ; 

my $pid = IPC::Open3::open3($wtr, $rdr, $err, @_); 

close($wtr); 

my $stdout = ''; 
my $stderr = ''; 

my $s = IO::Select->new; 

$s->add($rdr) if $rdr; 
$s->add($err) if $err; 

while (my @ready = $s->can_read) { 

    foreach my $ioh (@ready) { 

     my $bytes_read = sysread($ioh, my $chunk = '', 1024); 

     die "read error: $!" unless $bytes_read >= 0; 

     if ($bytes_read) { 
      ($ioh eq $rdr? $stdout: $stderr) .= $chunk; 
     } 
    else { 
      $s->remove($ioh); 
     } 
    } 
} 

my $pid1; 
for (;;) { 

    last if kill(0, $pid); 

    $pid1 = wait(); 
    # 
    # Wait until we see the process or -1 (no active processes); 
    # 
    last if ($pid1 == $pid || $pid1 <= 0); 
} 

Beenden Sie das Lesen, bevor Sie den Prozess beenden. Wenn Sie in die stdin des Prozesses schreiben, müssen Sie auch $ wtr und syswrite zur obigen Auswahlschleife hinzufügen.

EDIT

Begründung:

Die oben ist wahrscheinlich übertrieben für einfache Fälle. Diese erweiterte Handhabung von Eingabe und Ausgabe kommt ins Spiel, wenn Sie wahrscheinlich mehr als ein paar K Daten bewegen.

Sie würden es nicht benötigen, wenn Sie zum Beispiel einen Befehl 'df' ausführen würden.

Wenn jedoch Systempuffer für stdin, stdout oder stderr auffüllen, wird die Blockierung wahrscheinlich und die Dinge können mehr involviert werden.

Wenn der untergeordnete Prozess die Puffer stderr und/oder stdout füllt, wird er wahrscheinlich blockiert und darauf gewartet, dass Sie ihn löschen. Aber wenn Sie auf den Prozess warten, bevor Sie von stdout oder stderr lesen; Das ist eine Sackgasse. Sie werden wahrscheinlich feststellen, dass der Systemaufruf nie beendet wird und der untergeordnete Prozess nie abgeschlossen wird.

Es gibt eine ähnliche Möglichkeit des Deadlocks, wenn in stdin geschrieben wird, aber der untergeordnete Prozess kann die Eingabe nicht verarbeiten. Dies ist besonders wahrscheinlich in einer Pipe-Situation, in der der Child-Prozess Input verbraucht und in stdout schreibt.

Die Auswahlschleife löscht die Puffer progressiv, um Blockierungen zu vermeiden. Sowohl stdout als auch stderr werden gleichzeitig überwacht.

Wenn Sie in stdin schreiben und von stdout (einer Pipe) lesen, sollten Sie stdout und stderr deaktivieren und nur in stdin schreiben, wenn es bereit ist, Eingaben zu empfangen.

Einfach warten auf den Prozess zu beenden, dann funktioniert das Lesen von stdout/stderr wahrscheinlich 90% der Zeit. Diese Antwort dient nur dazu, Ihnen einen Ort zu geben, an den Sie gehen können, wenn die Dinge komplizierter werden und die Prozesse zu blockieren beginnen oder in eine Sackgasse geraten.

EDIT2

Wie für die Verwendung zu, würde ich sagen, beginnen einfach, Test hart.

Gehen Sie mit Sorpigals Ansatz, aber versuchen Sie, Test mit höheren Datenmengen und unter schwierigeren Lasten und Bedingungen, die Sie jemals in einem Live-System erwarten würde.

+0

Können Sie erklären, warum all dies benötigt wird? –

+0

@David B: Ich habe eine Erklärung hinzugefügt. Hoffe das hilft. – dwarring

Verwandte Themen