我是Perl noob,请原谅这个基本问题。我需要修改现有的Perl程序。我想通过外部程序管道一个字符串(可以包含多行)并读取该程序的输出。所以这个外部程序用于修改字符串。我们只需使用cat
作为过滤程序。我试过这样但它不起作用。 (cat
的输出转到stdout而不是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";
我已经读过Perl不支持它,因为它可能会陷入僵局,我可以理解它。但那我该怎么做呢?
答案 0 :(得分:24)
您可以使用IPC::Open3与儿童进行双向沟通。
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";
}
答案 1 :(得分:10)
这涉及系统编程,因此它不仅仅是一个基本问题。如上所述,您的主程序不需要与外部程序进行全双工交互。数据流沿一个方向传播,即
string→外部程序→主程序
创建此管道非常简单。 Perl的open
在“Safe pipe opens” section of the perlipc documentation中有一个有用的模式。
进程间通信的另一个有趣的方法是让你的单个程序进行多进程并在你自己之间进行通信。
open
函数将接受"-|"
或"|-"
的文件参数来做一件非常有趣的事情:它会分叉连接到您打开的文件句柄的子项。孩子正在运行与父母相同的程序。例如,当在假定的UID或GID下运行时,这对于安全地打开文件很有用。如果您打开一个减号管道,您可以写入您打开的文件句柄,您的孩子将在他的STDIN
中找到它。如果您从减号打开管道,您可以从您打开的文件句柄中读取您孩子写给STDOUT
的任何内容。
这是一个涉及管道的open
,它为返回值提供细微差别。 perlfunc documentation on open
解释说。
如果您在命令
-
上打开管道(即,使用|-
的单参数或双参数形式指定-|
或open
,完成隐式fork
,因此open返回两次:在父进程中它返回子进程的pid,在子进程中它返回(已定义的)0
。使用defined($pid)
或//
确定open
是否成功。
要创建脚手架,我们会在每个步骤使用open
到fork
一个新流程按从右到左的顺序工作。
fork
最终将成为外部程序的进程。fork
字符串打印过程,以使其输出到达STDIN
。exec
外部程序执行转换。exit
,它会提升到一个新的水平。完成所有这些工作后,你所要做的只是将你的建议植入底部,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: $!";
}
感谢您有机会撰写这样一个有趣的节目!
答案 2 :(得分:2)
您可以使用-n命令行开关有效地将现有程序代码包装在while循环中...查看-n的手册页:
LINE:
while (<>) {
... # your program goes here
}
然后您可以直接使用操作系统的管道机制
cat file | your_perl_prog.pl
(编辑) 我会试着更仔细地解释一下......
关于perl程序扮演什么角色的问题尚不清楚:过滤或最后阶段。这适用于任何一种情况,所以我认为它是后者。
'your_perl_prog.pl'是您现有的代码。我会把你的过滤程序称为“过滤器”。
修改your_perl_prog.pl,使shebang行添加'-n'开关:#!/ usr / bin / perl -n或#!/ bin / env“perl -n”
这有效地为your_perl_prog.pl中的代码提供了一段时间(&lt;&gt;){}循环
添加BEGIN块以打印标题:
BEGIN {print "HEADER LINE\n");}
您可以使用'$line = <>;'
和处理/打印
然后用
调用批次cat sourcefile |filter|your_perl_prog.pl
答案 3 :(得分:1)
我希望在不改变它的情况下扩展@Greg Bacon的答案。
我必须执行类似的操作,但想要编写代码 给定/ when命令,并且还发现有显式exit() 调用丢失,因为它在示例代码中掉线并退出。
我还必须让它也适用于运行ActiveState perl的版本, 但那个版本的perl不起作用。 请参阅此问题How to read to and write from a pipe in perl with 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: $!";
答案 4 :(得分:0)
执行OP所需的最简单的方法(不涉及所有这些很酷的内部方法)是使用一个临时文件来保存输出,直到完成外部处理器为止,如下所示:
open ToTemp, "|/usr/bin/tac>/tmp/MyTmp$$.whee" or die "open the tool: $!";
print ToTemp $TheMessageWhateverItIs;
close ToTemp;
my $Result = `cat /tmp/MyTmp$$.whee`; # or open and read it, or use File::Slurp, etc
unlink "/tmp/MyTmp$$.whee";
当然,这不适用于交互式的内容,但是协同例程似乎不在原始问题的范围之内。