2008-09-20 9 views
56

Ist es möglich, einen externen Prozess von Perl auszuführen, erfassen Sie seine stderr, stdout UND den Prozess Exit-Code?Wie erfassen Sie stderr, stdout und den Exit-Code auf einmal in Perl?

Ich bin in der Lage, Kombinationen von diesen zu tun, z.B. Verwenden Sie Backticks, um stdout zu erhalten, IPC :: Open3, um Ausgaben zu erfassen, und system(), um Ausgangscodes zu erhalten.

Wie erfassen Sie stderr, stdout und den Exit-Code auf einmal?

Antwort

26

Wenn Sie die Dokumentation für IPC :: Open3 erneut lesen, sehen Sie eine Notiz, dass Sie waitpid aufrufen sollten, um den untergeordneten Prozess zu ernten. Sobald Sie dies tun, sollte der Status in $? verfügbar sein. Der Ausgangswert ist $? >> 8. Siehe $? in perldoc perlvar.

+1

Ich weiß nicht, warum die Links vermasselt sind. Alles sieht gut aus in Bearbeitung/Vorschau. –

+2

Ich habe perl5porters einen Patch an IPC :: Open2 und :: Open3 geschickt, um die Waitpid-Sachen in der SYNOPSIS dieser Module anzuzeigen. :) –

7

Es gibt drei grundlegende Arten von externen Befehlen:

system $cmd;  # using system() 
$output = `$cmd`;  # using backticks (``) 
open (PIPE, "cmd |"); # using open() 

Mit system() sowohl STDOUT und STDERR die gleiche Stelle wie das Skript STDOUT und STDERR, es sei denn, der system() Befehl leitet sie gehen. Backticks und open() lesen Sie nur die STDOUT Ihres Befehls.

Sie könnten auch etwas wie die folgenden mit öffnen, um beide STDOUT und STDERR umleiten.

open(PIPE, "cmd 2>&1 |"); 

Der Rückgabecode wird immer in $? gespeichert, wie durch @Michael Carman zur Kenntnis genommen.

+0

Vergiss nicht 'qx //' –

41

(aktualisieren: Ich habe die API für IO :: CaptureOutput aktualisiert dies noch einfacher zu machen.)

Es gibt mehrere Möglichkeiten, dies zu tun. Hier ist eine Option, mit der IO::CaptureOutput Modul:

use IO::CaptureOutput qw/capture_exec/; 

my ($stdout, $stderr, $success, $exit_code) = capture_exec(@cmd); 

Dies ist die capture_exec() -Funktion, aber IO :: CaptureOutput hat auch eine allgemeinere capture() Funktion, die verwendet werden können, erfassen entweder Perl-Ausgang oder Ausgabe von externe Programme. Wenn also ein Perl-Modul ein externes Programm verwendet, erhalten Sie trotzdem die Ausgabe.

Es bedeutet auch, dass Sie sich nur einen einzigen Ansatz zur Erfassung von STDOUT und STDERR merken (oder zusammenführen) müssen, anstatt IPC :: Open3 für externe Programme und andere Module zur Erfassung von Perl-Ausgaben zu verwenden.

+12

Es scheint [Capture :: Tiny] (http://search.cpan.org/dist/Capture-Tiny/) ist neuer und besser: 'Dieses Modul wurde inspiriert von IO :: CaptureOutput, das ähnliche Funktionalität ohne die Möglichkeit bietet, Ausgabe auszugeben und mit komplizierterem Code und API. IO :: CaptureOutput verarbeitet keine Layer oder die meisten ungewöhnlichen Fälle, die im Abschnitt "Einschränkungen" beschrieben sind, und ich empfehle es nicht mehr. - http://search.cpan.org/~dagolden/Capture-Tiny-0.18/lib/Capture/Tiny.pm#SEE_ALSO –

+0

Ein Anwendungsbeispiel finden Sie unter http://StackOverflow.com/a/8781408/110126 Erfassen :: Tiny. – buzz3791

+5

@PhilipDurbin Es ist witzig, dass der Autor dieser Antwort der gleiche Typ ist wie der Autor von Capture :: Tiny. :) – simbabque

14

Wenn Sie nicht über den Inhalt der STDERR wollen, dann ist die Erfassung() Befehl aus IPC::System::Simple Modul ist fast genau, was Sie nach:

use IPC::System::Simple qw(capture system $EXITVAL); 

    my $output = capture($cmd, @args); 

    my $exit_value = $EXITVAL; 

können Sie Capture() verwenden, mit einem einzigen Argument um die Shell aufzurufen, oder mehrere Argumente, um die Shell zuverlässig zu vermeiden. Es gibt auch capturex(), die niemals die Shell aufruft, auch nicht mit einem einzigen Argument.

Im Gegensatz zu den integrierten System- und Backtick-Befehlen von Perl gibt IPC :: System :: Simple den vollständigen 32-Bit-Exitwert unter Windows zurück. Es wird auch eine detaillierte Ausnahme ausgelöst, wenn der Befehl nicht gestartet werden kann, in ein Signal übergeht oder einen unerwarteten Exit-Wert zurückgibt.Das bedeutet für viele Programme, anstatt zu prüfen, die Ausfahrt selbst Werte, können Sie verlassen sich auf IPC :: System :: Simple für Sie die harte Arbeit zu tun:

use IPC::System::Simple qw(system capture $EXIT_ANY); 

system([0,1], "frobincate", @files);  # Must return exitval 0 or 1 

my @lines = capture($EXIT_ANY, "baznicate", @files); # Any exitval is OK. 

foreach my $record (@lines) { 
    system([0, 32], "barnicate", $record); # Must return exitval 0 or 32 
} 

IPC :: System :: Simple ist rein Perl, hat keine Abhängigkeiten und funktioniert sowohl auf Unix- als auch auf Windows-Systemen. Leider bietet es keine Möglichkeit, STDERR zu erfassen, daher ist es möglicherweise nicht für alle Ihre Bedürfnisse geeignet.

IPC::Run3 bietet eine saubere und einfache Schnittstelle für die Wiederherstellung aller drei gängigen Dateihandles, aber leider überprüft es nicht, ob der Befehl erfolgreich war, also müssen Sie $ überprüfen? manuell, was überhaupt nicht Spaß macht. Bereitstellung einer öffentlichen Schnittstelle zur Überprüfung von $? ist etwas, das auf meinem to-do list für IPC :: System :: Simple ist, seit der Überprüfung von $? plattformübergreifend ist keine Aufgabe, die ich jemandem wünsche.

Im Namespace IPC:: befinden sich weitere Module, die Sie ebenfalls unterstützen können. YMMV.

Alles Gute,

Paul

+0

Technisch hat es eine Abhängigkeit, aber nur unter Windows, wo es Win32 :: Process benötigt. Während dies nicht im Kern Perl ist, ist es mit den ActiveState Perl und Strawberry Perl Distributionen gebündelt. – xdg

+0

+1, ich benutze IPC :: System :: Simple die ganze Zeit. – Ether

0

Wenn Sie wirklich kompliziert bekommen, möchten Sie vielleicht Expect.pm versuchen. Aber das ist wahrscheinlich zu viel Aufwand, wenn Sie nicht auch das Senden von Eingaben an den Prozess verwalten müssen.

0

Ich fand IPC:run3, um sehr hilfreich zu sein. Sie können alle untergeordneten Pipes an einen Glob oder eine Variable weiterleiten. sehr leicht! Und der Exit-Code wird in $ gespeichert.

Unten ist, wie ich stderr packte, die ich wusste, wäre eine Nummer. Die cmd gab informatische Transformationen nach stdout aus (die ich mit> in eine Datei in den Argumenten piped) und berichtete, wie viele Transformationen zu STDERR.

use IPC::Run3 

my $number; 
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number); 
die "Command failed: $!" unless ($run && $? == 0);