webentwicklung-frage-antwort-db.com.de

Wie erfassen Sie stderr, stdout und den Exit-Code gleichzeitig in Perl?

Ist es möglich, einen externen Prozess von Perl aus auszuführen, stderr, stdout UND den Prozess-Exit-Code zu erfassen?

Ich scheine in der Lage zu sein, Kombinationen davon zu machen, z. Verwenden Sie Backticks, um stdout zu erhalten, IPC :: Open3, um Ausgaben zu erfassen, und system (), um Beendigungscodes zu erhalten.

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

56
Scooby

Wenn Sie die Dokumentation für IPC :: Open3 erneut lesen, wird ein Hinweis angezeigt, dass Sie waitpid aufrufen sollten, um den untergeordneten Prozess zu ernten. Sobald Sie dies tun, sollte der Status in $? verfügbar sein. Der Exit-Wert ist $? >> 8. Siehe $? in perldoc perlvar .

26
Michael Carman

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

Dafür gibt es mehrere Möglichkeiten. Hier ist eine Option mit dem Modul IO :: CaptureOutput :

use IO::CaptureOutput qw/capture_exec/;

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

Dies ist die Funktion capture_exec (), aber IO :: CaptureOutput verfügt auch über eine allgemeinere capture () - Funktion, mit der entweder die Perl-Ausgabe oder die Ausgabe von externen Programmen erfasst werden kann. Wenn also ein Perl-Modul ein externes Programm verwendet, erhalten Sie immer noch die Ausgabe.

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

41
xdg

Wenn Sie den Inhalt von STDERR nicht möchten, ist der capture () - Befehl von IPC :: System :: Simple module fast genau das, was Sie suchen:

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

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

   my $exit_value = $EXITVAL;

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

Im Gegensatz zu den integrierten System- und Backticks-Befehlen von Perl gibt IPC :: System :: Simple den vollständigen 32-Bit-Exit-Wert unter Windows zurück. Es wird auch eine detaillierte Ausnahme ausgelöst, wenn der Befehl nicht gestartet werden kann, an einem Signal stirbt oder einen unerwarteten Beendigungswert zurückgibt. Für viele Programme bedeutet dies, dass Sie sich nicht auf die Exitwerte selbst überprüfen, sondern sich auf IPC :: System :: Simple verlassen können, um die harte Arbeit für Sie zu erledigen:

 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 reines Perl, hat keine Abhängigkeiten und funktioniert sowohl auf Unix- als auch auf Windows-Systemen. Leider gibt es keine Möglichkeit, STDERR zu erfassen, daher ist es möglicherweise nicht für alle Ihre Anforderungen geeignet.

IPC :: Run3 bietet eine saubere und einfache Schnittstelle, um alle drei gängigen Dateihandles neu zu installieren. Leider wird nicht überprüft, ob der Befehl erfolgreich war. Sie müssen also $ überprüfen? manuell, was überhaupt keinen Spaß macht. Bereitstellung einer öffentlichen Schnittstelle für die Prüfung von $? ist etwas, das auf meiner To-Do-Liste für IPC :: System :: Simple steht, seit $ untersucht hat? plattformübergreifend ist keine Aufgabe, die ich jedem wünschen würde.

Der Namespace IPC :: enthält noch weitere Module, die Ihnen möglicherweise weiterhelfen. YMMV.

Alles Gute,

Paul

14
pjf

Es gibt drei grundlegende Möglichkeiten, externe Befehle auszuführen:

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

Mit system() gehen sowohl STDOUT als auch STDERR an dieselbe Stelle wie STDOUT und STDERR, des Skripts, sofern der system()-Befehl sie nicht umleitet. Backticks und open() lesen nur die STDOUT Ihres Befehls.

Sie könnten auch mit open so etwas wie das folgende aufrufen, um sowohl STDOUT als auch STDERR umzuleiten. 

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

Der Rückkehrcode wird immer in $? gespeichert, wie von @Michael Carman angegeben.

7
hoyhoy

Wenn Sie wirklich kompliziert werden, sollten Sie Expect.pm ausprobieren. Aber das ist wahrscheinlich übertrieben, wenn Sie nicht auch das Senden von Eingaben an den Prozess verwalten müssen.

0
skiphoppy

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

Unten ist, wie ich stderr packte, von dem ich wusste, dass es eine Zahl wäre. Das Cmd gab informatische Umwandlungen in stdout aus (die mit> in eine Datei in den Argumenten geleitet wurden) und meldete, wie viele Umwandlungen in STDERR enthalten waren.

use IPC::Run3

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