Je suis un perl noob, veuillez donc excuser cette question de base. J'ai besoin de modifier un programme Perl existant. Je veux diriger une chaîne (qui peut contenir plusieurs lignes) via un programme externe et lire la sortie de ce programme. Donc, ce programme externe est utilisé pour modifier la chaîne. Utilisons simplement cat
comme programme de filtrage. Je l'ai essayé comme ça mais ça ne marche pas. (La sortie de cat
va à stdout au lieu d'être lue par Perl
.)
#!/usr/bin/Perl
open(MESSAGE, "| cat |") or die("cat failed\n");
print MESSAGE "Line 1\nLine 2\n";
my $message = "";
while (<MESSAGE>)
{
$message .= $_;
}
close(MESSAGE);
print "This is the message: $message\n";
J'ai lu que cela n'est pas pris en charge par Perl car cela peut se retrouver dans une impasse et je peux le comprendre. Mais comment dois-je faire alors?
Vous pouvez utiliser IPC :: Open3 pour établir une communication bidirectionnelle avec l'enfant.
use strict;
use IPC::Open3;
my $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, 'cat')
or die "open3() failed $!";
my $r;
for(my $i=1;$i<10;$i++) {
print CHLD_IN "$i\n";
$r = <CHLD_OUT>;
print "Got $r from child\n";
}
Cela implique une programmation système, c'est donc plus qu'une question de base. Tel qu'il est écrit, votre programme principal ne nécessite pas d'interaction en duplex intégral avec le programme externe. Le flux de données se déplace dans une seule direction, à savoir
chaîne → programme externe → programme principal
La création de ce pipeline est simple. open
de Perl a un mode utile expliqué dans la section "Ouverture sécurisée du tuyau" de la documentation de perlipc .
Une autre approche intéressante de la communication interprocessus consiste à rendre votre programme unique multiprocessus et à communiquer entre - ou même entre - vous-mêmes. La fonction
open
acceptera un argument de fichier de"-|"
Ou"|-"
Pour faire quelque chose de très intéressant: cela entraînera un enfant connecté au descripteur de fichier que vous avez ouvert. L'enfant exécute le même programme que le parent. Cela est utile pour ouvrir un fichier en toute sécurité lors de l'exécution sous un UID ou GID supposé, par exemple. Si vous ouvrez un canal vers moins, vous pouvez écrire dans le descripteur de fichier que vous avez ouvert et votre enfant le trouvera dans sonSTDIN
. Si vous ouvrez un tube depuis moins, vous pouvez lire à partir du descripteur de fichier que vous avez ouvert tout ce que votre enfant écrit dans sonSTDOUT
.
Il s'agit d'un open
qui implique un tube, ce qui donne des nuances à la valeur de retour. La documentation perlfunc sur open
explique.
Si vous ouvrez un canal sur la commande
-
(C'est-à-dire, spécifiez|-
Ou-|
Avec les formes à un ou deux arguments deopen
) , unfork
implicite est fait, donc open renvoie deux fois: dans le processus parent, il renvoie le pid du processus enfant, et dans le processus enfant, il retourne (un défini)0
. Utilisezdefined($pid)
ou//
Pour déterminer siopen
a réussi.
Pour créer l'échafaudage, nous travaillons de droite à gauche en utilisant open
à fork
un nouveau processus à chaque étape.
fork
un processus qui deviendra éventuellement le programme externe.fork
le processus d'impression de chaîne afin de faire arriver sa sortie sur notre STDIN
.exec
le programme externe pour effectuer sa transformation.exit
, qui passe au niveau suivant.Avec tout cela mis en place, tout ce que vous avez à faire est d'implanter votre suggestion au fond, M. Cobb.
#! /usr/bin/env Perl
use 5.10.0; # for defined-or and given/when
use strict;
use warnings;
my @transform = qw( tr [A-Za-z] [N-ZA-Mn-za-m] ); # rot13
my @inception = (
"V xabj, Qnq. Lbh jrer qvfnccbvagrq gung V pbhyqa'g or lbh.",
"V jnf qvfnccbvagrq gung lbh gevrq.",
);
sub snow_fortress { print map "$_\n", @inception }
sub hotel {
given (open(STDIN, "-|") // die "$0: fork: $!") { # / StackOverflow hiliter
snow_fortress when 0;
exec @transform or die "$0: exec: $!";
}
}
given (open(my $fh, "-|") // die "$0: fork: $!") {
hotel when 0;
print while <$fh>;
close $fh or warn "$0: close: $!";
}
Merci pour l'opportunité d'écrire un programme aussi amusant!
Vous pouvez utiliser le commutateur de ligne de commande -n pour encapsuler efficacement votre code de programme existant dans une boucle while ... regardez la page de manuel pour -n:
LINE:
while (<>) {
... # your program goes here
}
Ensuite, vous pouvez utiliser directement le mécanisme de tuyau du système d'exploitation
cat file | your_Perl_prog.pl
(Edit) Je vais essayer d'expliquer cela plus attentivement ...
La question n'est pas claire de savoir quel rôle joue le programme Perl: filtre ou étape finale. Cela fonctionne dans les deux cas, donc je suppose que c'est le dernier.
'your_Perl_prog.pl' est votre code existant. J'appellerai votre programme de filtrage "filtre".
Modifiez your_Perl_prog.pl pour que la ligne Shebang ait un commutateur '-n' ajouté: #!/Usr/bin/Perl -n ou #!/Bin/env "Perl -n"
Cela met effectivement une boucle while (<>) {} autour du code dans your_Perl_prog.pl
ajoutez un bloc BEGIN pour imprimer l'en-tête:
BEGIN {print "HEADER LINE\n");}
Vous pouvez lire chaque ligne avec '$line = <>;'
et traiter/imprimer
Appelez ensuite le lot avec
cat sourcefile |filter|your_Perl_prog.pl
Je veux développer la réponse de @Greg Bacon sans la changer.
J'ai dû exécuter quelque chose de similaire, mais je voulais coder sans les commandes given/when, et j'ai également constaté qu'il manquait des appels explicites exit () parce que dans l'exemple de code, il était tombé à travers et s'était arrêté.
J'ai également dû le faire fonctionner également sur une version exécutant ActiveState Perl, mais cette version de Perl ne fonctionne pas. Voir cette question Comment lire et écrire depuis un tube en Perl avec ActiveState Perl?
#! /usr/bin/env Perl
use strict;
use warnings;
my $isActiveStatePerl = defined(&Win32::BuildNumber);
sub pipeFromFork
{
return open($_[0], "-|") if (!$isActiveStatePerl);
die "active state Perl cannot cope with dup file handles after fork";
pipe $_[0], my $child or die "cannot create pipe";
my $pid = fork();
die "fork failed: $!" unless defined $pid;
if ($pid) { # parent
close $child;
} else { # child
open(STDOUT, ">&=", $child) or die "cannot clone child to STDOUT";
close $_[0];
}
return $pid;
}
my @transform = qw( tr [A-Za-z] [N-ZA-Mn-za-m] ); # rot13
my @inception = (
"V xabj, Qnq. Lbh jrer qvfnccbvagrq gung V pbhyqa'g or lbh.",
"V jnf qvfnccbvagrq gung lbh gevrq.",
);
sub snow_fortress { print map "$_\n", @inception }
sub hotel
{
my $fh;
my $pid = pipeFromFork($fh); # my $pid = open STDIN, "-|";
defined($pid) or die "$0: fork: $!";
if (0 == $pid) {
snow_fortress;
exit(0);
}
open(STDIN, "<&", $fh) or die "cannot clone to STDIN";
exec @transform or die "$0: exec: $!";
}
my $fh;
my $pid = pipeFromFork($fh); # my $pid = open my $fh, "-|";
defined($pid) or die "$0: fork: $!";
if (0 == $pid) {
hotel;
exit(0);
}
print while <$fh>;
close $fh or warn "$0: close: $!";