56 votes

Comment capturer stderr, stdout, et le code de sortie en une seule fois, en Perl ?

Est-il possible d'exécuter un processus externe depuis Perl, de capturer son stderr, son stdout ET le code de sortie du processus ?

Il semble que je puisse faire des combinaisons de ces éléments, par exemple utiliser des backticks pour obtenir stdout, IPC::Open3 pour capturer les sorties, et system() pour obtenir les codes de sortie.

Comment capturer stderr, stdout, et le code de sortie en une seule fois ?

41voto

xdg Points 1788

( Mise à jour : J'ai mis à jour l'API pour IO::CaptureOutput pour rendre cela encore plus facile).

Il existe plusieurs façons de procéder. Voici une option, qui consiste à utiliser le IO::CaptureOutput module :

use IO::CaptureOutput qw/capture_exec/;

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

Il s'agit de la fonction capture_exec(), mais IO::CaptureOutput possède également une fonction capture() plus générale qui peut être utilisée pour capturer la sortie Perl ou la sortie de programmes externes. Ainsi, si un module Perl utilise un programme externe, vous obtenez toujours la sortie.

Cela signifie également que vous ne devez vous souvenir que d'une seule approche pour capturer STDOUT et STDERR (ou les fusionner) au lieu d'utiliser IPC::Open3 pour les programmes externes et d'autres modules pour capturer la sortie Perl.

26voto

Michael Carman Points 21983

Si vous relisez la documentation de IPC::Open3, vous verrez une note indiquant que vous devez appeler waitpid pour récolter le processus de l'enfant. Une fois que vous avez fait cela, le statut devrait être disponible en $? . La valeur de sortie est $? >> 8 . Voir $? dans perldoc perlvar .

14voto

pjf Points 4685

Si vous ne voulez pas le contenu de STDERR, alors la commande capture() de IPC::System::Simple est presque exactement ce que vous recherchez :

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

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

   my $exit_value = $EXITVAL;

Vous pouvez utiliser capture() avec un seul argument pour invoquer l'interpréteur de commandes, ou plusieurs arguments pour éviter l'interpréteur de commandes de manière fiable. Il y a aussi capturex() qui n'appelle jamais l'interpréteur de commandes, même avec un seul argument.

Contrairement aux commandes système et backticks intégrées de Perl, IPC::System::Simple renvoie la valeur de sortie 32 bits complète sous Windows. Il lève également une exception détaillée si la commande ne peut pas être lancée, meurt à un signal, ou renvoie une valeur de sortie inattendue. Cela signifie que pour de nombreux programmes, plutôt que de vérifier vous-même les valeurs de sortie, vous pouvez vous fier à IPC::System::Simple pour faire le travail à votre place :

 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 est purement Perl, n'a pas de dépendances, et fonctionne à la fois sur les systèmes Unix et Windows. Malheureusement, il ne fournit pas de moyen de capturer STDERR, donc il peut ne pas convenir à tous vos besoins.

IPC::Run3 fournit une interface propre et facile pour replomber les trois gestionnaires de fichiers courants, mais malheureusement, il ne vérifie pas si la commande a réussi, vous devrez donc inspecter $ ? manuellement, ce qui n'est pas du tout amusant. Fournir une interface publique pour inspecter $ ? est quelque chose qui fait partie de mes priorités. liste des tâches à accomplir pour IPC::System::Simple, car inspecter $ ? de manière multiplateforme n'est pas une tâche que je souhaite à quiconque.

Il existe d'autres modules dans le IPC: : qui pourrait également vous aider. YMMV.

Tous les meilleurs,

Paul

7voto

hoyhoy Points 3806

Il existe trois méthodes de base pour exécuter des commandes externes :

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

Con system() les deux STDOUT et STDERR iront au même endroit que le script. STDOUT y STDERR, à moins que le system() les redirige. Les backticks et open() ne lire que le STDOUT de votre commandement.

Vous pourriez également appeler quelque chose comme ce qui suit avec open pour rediriger les deux STDOUT y STDERR .

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

Le code de retour est toujours stocké dans $? comme l'a noté @Michael Carman .

0voto

skiphoppy Points 16563

Si vous vous compliquez vraiment la vie, vous pouvez essayer Expect.pm. Mais c'est probablement trop compliqué si vous n'avez pas besoin de gérer également l'envoi des entrées au processus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X