Comment capturer les commandes stdin et stdout de la commande système à partir d'un script Perl?
Question
Au milieu d'un script Perl, il y a une commande système que je veux exécuter. J'ai une chaîne qui contient les données qui doivent être introduites dans stdin (la commande n'accepte que les entrées de stdin), et j'ai besoin de capturer la sortie écrite dans stdout. J'ai examiné différentes méthodes d'exécution de commandes système en Perl, et la fonction open
semble être ce dont j'ai besoin, à ceci près qu'il semble que je ne peux capturer que stdin ou stdout, pas les deux.
Pour le moment, il semble que ma meilleure solution consiste à utiliser open
, à rediriger stdout dans un fichier temporaire et à le lire à la fin de la commande. Y a-t-il une meilleure solution?
La solution
Je pense que vous souhaitez jeter un oeil à IPC :: Open2
Autres conseils
IPC :: Open2 / 3, ça va, mais j’ai découvert que tout ce dont j’ai besoin est en général IPC :: Run3 , qui gère très bien les cas simples avec une complexité minimale:
use IPC::Run3; # Exports run3() by default
run3( \@cmd, \$in, \$out, \$err );
La documentation compare IPC :: Run3 à d'autres alternatives. Cela vaut la peine d'être lu même si vous ne décidez pas de l'utiliser.
La la documentation perlipc couvre de nombreuses manières de le faire, y compris IPC :: Open2 et IPC :: Open3.
Quelque part en haut de votre script, insérez la ligne
use IPC::Open2;
Cela inclura le module nécessaire, généralement installé avec la plupart des distributions Perl par défaut. (Si vous ne l'avez pas, vous pouvez l'installer à l'aide de CPAN.) Ensuite, au lieu d'ouvrir, appelez:
$pid = open2($cmd_out, $cmd_in, 'some cmd and args');
Vous pouvez envoyer des données à votre commande en l'envoyant à $ cmd_in, puis lire le résultat de votre commande en lisant à partir de $ cmd_out.
Si vous souhaitez également pouvoir lire le flux stderr de la commande, vous pouvez utiliser le module IPC :: Open3 à la place.
IPC :: Open3 ferait probablement ce que vous voulez. Il peut capturer STDERR et STDOUT.
Un moyen très facile de faire que j'ai récemment découvert est le IPC :: Filtre module . Cela vous permet de faire le travail de manière extrêmement intuitive:
$output = filter $input, 'somecmd', '--with', 'various=args', '--etc';
Notez comment il appelle votre commande sans passer par le shell si vous lui transmettez une liste. Il effectue également un travail raisonnable de gestion des erreurs pour les utilitaires courants. (En cas d'échec, il meurt
, en utilisant le texte de STDERR comme message d'erreur; en cas de succès, STDERR est simplement ignoré.)
Bien sûr, il ne convient pas à d'énormes quantités de données car il ne permet aucun traitement en continu; En outre, la gestion des erreurs peut ne pas être suffisamment granulaire pour répondre à vos besoins. Mais cela rend les nombreux cas simples vraiment vraiment simples.
Il existe une commande Perl spéciale pour cela
open2()
Plus d’informations sur: http://sunsite.ualberta.ca/Documentation/Misc/perl-5.6.1/lib/IPC/Open2.html
Si vous ne souhaitez pas inclure de packages supplémentaires, vous pouvez simplement faire
open(TMP,">tmpfile");
print TMP $tmpdata ;
open(RES,"$yourcommand|");
$res = "" ;
while(<RES>){
$res .= Si vous ne souhaitez pas inclure de packages supplémentaires, vous pouvez simplement faire
<*>
ce qui est le contraire de ce que vous avez suggéré, mais devrait également fonctionner.
;
}
ce qui est le contraire de ce que vous avez suggéré, mais devrait également fonctionner.
Je le fais toujours de cette façon si je n'attends qu'une seule ligne de sortie ou si je souhaite fractionner le résultat sur autre chose qu'une nouvelle ligne:
my $result = qx( command args 2>&1 );
my $rc=$?;
# $rc >> 8 is the exit code of the called program.
if ($rc != 0 ) {
error();
}
Si vous souhaitez gérer une réponse sur plusieurs lignes, obtenez le résultat sous forme de tableau:
my @lines = qx( command args 2>&1 );
foreach ( my $line ) (@lines) {
if ( $line =~ /some pattern/ ) {
do_something();
}
}