私はPerlの初心者なので、この基本的な質問を許しません。既存のPerlプログラムを変更する必要があります。文字列(複数の行を含めることができる)を外部プログラムにパイプし、このプログラムからの出力を読み取りたい。したがって、この外部プログラムは文字列を変更するために使用されます。単純にcat
をフィルタープログラムとして使用します。このように試してみましたが、うまくいきません。 (cat
の出力は、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ではサポートされていません。しかし、それではどうすればよいですか?
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";
}
これにはシステムプログラミングが含まれるため、基本的な質問以上のものです。書かれているように、メインプログラムは外部プログラムとの全二重通信を必要としません。データフローは一方向、つまり
文字列→外部プログラム→メインプログラム
このパイプラインの作成は簡単です。 Perlのopen
には、perlipcドキュメントの 「Safe pipe opens」セクションで説明されている便利なモードがあります 。
プロセス間通信のもう1つの興味深いアプローチは、単一のプログラムをマルチプロセスにして、自分自身の間で、または自分自身の間でさえ通信することです。
open
関数は、_"-|"
_または_"|-"
_のいずれかのファイル引数を受け入れて、非常に興味深い処理を実行します。開いたファイルハンドルに接続された子をフォークします。子は親と同じプログラムを実行しています。これは、たとえば、想定されたUIDまたはGIDで実行しているときにファイルを安全に開く場合に役立ちます。マイナスへのパイプを開くと、開いたファイルハンドルに書き込むことができ、子供はSTDIN
でそれを見つけます。マイナスからパイプを開くと、子供がSTDOUT
に書き込んだものをすべて、開いたファイルハンドルから読み取ることができます。
これは、パイプを含むopen
であり、戻り値にニュアンスを与えます。 perlfuncのドキュメントopen
で説明しています。
コマンド_
-
_でパイプを開く場合(つまり、open
の1つまたは2つの引数の形式で_|-
_または_-|
_を指定します)、暗黙のfork
が完了すると、openは2度戻ります。親プロセスでは子プロセスのpidを返し、子プロセスでは(定義済み)_0
_を返します。open
が成功したかどうかを判断するには、defined($pid)
または_//
_を使用します。
足場を作成するには、各ステップでopen
からfork
の新しいプロセスを使用して、右から左の順序で作業します。
fork
は最終的に外部プログラムになるプロセスです。fork
に到達させるための文字列印刷プロセスSTDIN
です。exec
します。exit
を実行して、次のレベルに進みます。これらすべての設定が完了したら、コブ氏の提案を下部に埋め込むだけです。
_#! /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: $!";
}
_
そのような楽しいプログラムを書く機会をありがとう!
-nコマンドラインスイッチを使用すると、whileループで既存のプログラムコードを効果的にラップできます。-nのマニュアルページをご覧ください。
LINE:
while (<>) {
... # your program goes here
}
次に、オペレーティングシステムのパイプメカニズムを直接使用できます。
cat file | your_Perl_prog.pl
(編集)私はこれをより注意深く説明しようとします...
質問は、Perlプログラムがどのような役割を果たしているかについては明確ではありません:フィルターまたは最終段階。これはどちらの場合でも機能するので、後者だと思います。
「your_Perl_prog.pl」は既存のコードです。フィルタープログラムを 'filter'と呼びます。
Your_Perl_prog.plを変更して、Shebang行に「-n」スイッチを追加します。#!/ usr/bin/Perl -nまたは#!/ bin/env "Perl -n"
これにより、your_Perl_prog.plのコードの周りにwhile(<>){}ループが効果的に配置されます
bEGINブロックを追加してヘッダーを印刷します。
BEGIN {print "HEADER LINE\n");}
'$line = <>;'
で各行を読み取り、処理/印刷できます
次に、ロットを呼び出します
cat sourcefile |filter|your_Perl_prog.pl
@Greg Baconの答えを変えずに拡大したいと思います。
私は同じようなものを実行する必要がありましたが、given/whenコマンドなしでコーディングしたいと思いました。また、サンプルコードでは抜け落ちて終了したため、exit()呼び出しが明示的に欠落していることもわかりました。
ActiveState Perlを実行しているバージョンでも動作させる必要がありましたが、そのバージョンのPerlは動作しません。この質問を参照してください Perlで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: $!";