web-dev-qa-db-fra.com

Comment capturez-vous stderr, stdout et le code de sortie en une fois, en Perl?

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

Je semble être capable de faire des combinaisons de ceux-ci, par exemple. utilisez backticks pour obtenir stdout, IPC :: Open3 pour capturer les sorties et system () pour obtenir les codes de sortie.

Comment capturez-vous stderr, stdout et le code de sortie en même temps?

56
Scooby

Si vous relisez la documentation pour IPC :: Open3, une note vous invitant à appeler waitpid pour récupérer le processus enfant. Une fois que vous faites cela, le statut devrait être disponible dans $?. La valeur de sortie est $? >> 8. Voir $? in perldoc perlvar .

26
Michael Carman

(Update: j'ai mis à jour l'API pour IO :: CaptureOutput afin de rendre cela encore plus facile.)

Il y a plusieurs moyens de le faire. Voici une option, en utilisant le module IO :: CaptureOutput :

use IO::CaptureOutput qw/capture_exec/;

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

C'est la fonction capture_exec (), mais IO :: CaptureOutput a également une fonction plus générale capture () qui peut être utilisée pour capturer la sortie Perl ou la sortie de programmes externes. Donc, si un module Perl utilise un programme externe, vous obtenez toujours le résultat.

Cela signifie également que vous ne devez retenir qu'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 de capture de la sortie Perl.

41
xdg

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

   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 appeler le shell ou plusieurs arguments pour éviter le shell de manière fiable. Il y a aussi capturex () qui n'appelle jamais le shell, 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 démarré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 compter.

 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 un pur Perl, n'a pas de dépendances et fonctionne à la fois sur les systèmes Unix et Windows. Malheureusement, il ne permet pas de capturer STDERR et peut donc ne pas être adapté à tous vos besoins.

IPC :: Run3 fournit une interface simple et propre à la plomberie des trois descripteurs de fichiers courants, mais malheureusement, il ne vérifie pas si la commande a abouti. Vous devez donc inspecter $? manuellement, ce qui n'est pas du tout amusant. Fournir une interface publique pour inspecter $? est quelque chose qui est sur ma liste à faire pour IPC :: System :: Simple, depuis inspecter $? de manière multiplateforme, ce n’est une tâche que je ne souhaite à personne.

Il existe d’autres modules dans l’espace de nom IPC :: qui peuvent également vous aider. YMMV.

Bonne chance,

Paul

14
pjf

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()

Avec system(), STDOUT et STDERR auront le même emplacement que les variables STDOUT et STDERR, du script, sauf si la commande system() les redirige. Les Backticks et open() ne lisent que la STDOUT de votre commande.

Vous pouvez également appeler quelque chose comme ce qui suit avec open pour rediriger STDOUT et STDERR

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

Le code de retour est toujours stocké dans $? comme indiqué par @Michael Carman .

7
hoyhoy

Si vous devenez vraiment compliqué, essayez Expect.pm. Mais c'est probablement trop cher si vous n'avez pas besoin de gérer également l'envoi de données au processus.

0
skiphoppy

J'ai trouvé IPC: run3 très utile. Vous pouvez transférer tous les canaux enfants vers un glob ou une variable. très facilement! Et le code de sortie sera stocké dans $ ?.

Ci-dessous est comment j'ai attrapé stderr que je savais être un nombre. La cmd a généré des transformations informatiques en stdout (que j’ai redirigé vers un fichier dans les arguments en utilisant>) et indiqué combien de transformations en STDERR.

use IPC::Run3

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