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

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

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

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

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

( Mise à jour : j’ai mis à jour l’API pour IO :: CaptureOutput pour 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 de capture () plus générale 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 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.

Si vous ne voulez pas le contenu de STDERR, alors la commande capture () du module 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 appeler le shell ou plusieurs arguments pour éviter de manière fiable le shell. 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 complète de 32 bits sous Windows. Il génère é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 beaucoup de programmes, plutôt que de vérifier vous-même les valeurs de sortie, vous pouvez compter sur IPC :: System :: Simple pour faire le travail difficile pour vous:

  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 de la pure Perl, ne comporte aucune dépendance et fonctionne à la fois sur les systèmes Unix et Windows. Malheureusement, cette méthode ne permet pas de capturer STDERR, elle ne convient donc peut-être pas à tous vos besoins.

IPC :: Run3 fournit une interface propre et facile pour replacer les trois descripteurs 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 est sur ma liste de tâches pour IPC :: System :: Simple, depuis l’inspection de $? Je ne souhaite à personne que ce soit d’une manière multi-plateforme.

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

Bonne chance,

Paul

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 iront tous deux au même endroit que STDOUT et STDERR, du script STDERR, moins que la commande system() redirige. Backticks et open() ne lisent que le STDOUT de votre commande.

Vous pouvez également appeler ce qui suit avec open pour redirect à la fois STDOUT et STDERR .

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

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

Si vous êtes vraiment compliqué, vous pouvez essayer Expect.pm. Mais c’est probablement exagéré si vous n’avez pas besoin de gérer également l’envoi des entrées au processus.

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

Voici comment je me suis emparé de stderr que je savais être un numéro. Le cmd génère des transformations informatiques en stdout (que j’ai acheminées vers un fichier dans les arguments à l’aide de>) et indique le nombre de transformations de STDERR.

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