open3のドキュメントを確認しましたが、理解できなかった部分は次のとおりです。
子のstdoutライターとそのstderrライターから読み取ろうとすると、ブロックに問題が発生します。つまり、select()またはIO :: Selectを使用する必要があります。これは、sysread(を使用するのが最適であることを意味します。 )通常のもののreadline()の代わりに。
永久にブロックする可能性があるため、これは非常に危険です。bcのようなものと通信し、書き込みと読み取りの両方を行うことを前提としています。bcのようなコマンドが一度に1行を読み取り、一度に1行を出力することを「知っている」ので、これはおそらく安全です。ただし、最初に入力ストリーム全体を読み取るsortのようなプログラムは、デッドロックを引き起こす傾向があります。
open3
だから私はそれをもっとよく知ることを望んで試してみました。これが最初の試みです:
sub hung_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
if(<$err>) {
print "[ERROR] : $_" while(<$err>);
die;
}
print "[OUTPUT]: $_" while (<$out>);
}
ここで初期化する必要があることに注意して$err
ください。
とにかく、これは4096文字(私のマシンの制限)を超えるテキストファイルであるとexecute("sort $some_file");
指定するとハングします。$some_file
次に、このFAQを調べました。以下は、私の新しいバージョンのexecuteです。
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $in = gensym();
#---------------------------------------------------
# using $in, $out doesn't work. it expects a glob?
local *OUT = IO::File->new_tmpfile;
local *ERR = IO::File->new_tmpfile;
my $pid = open3($in, ">&OUT", ">&ERR", $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
seek $_, 0, 0 for \*OUT, \*ERR;
if(<ERR>) {
print "[ERROR] : $_" while(<ERR>);
die;
}
print "[OUTPUT]: $_" while (<OUT>);
}
sort
コマンドは正常に実行されますが、理由がわかりません。
[更新] @tchristの答えを読んだ後、私は読んだ、そしてもう少しグーグルした後、このバージョンの:IO::Select
を思いついた。execute
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
my $sel = new IO::Select;
$sel->add($out, $err);
while(my @fhs = $sel->can_read) {
foreach my $fh (@fhs) {
my $line = <$fh>;
unless(defined $line) {
$sel->remove($fh);
next;
}
if($fh == $out) {
print "[OUTPUT]: $line";
}elsif($fh == $err) {
print "[ERROR] : $line";
}else{
die "[ERROR]: This should never execute!";
}
}
}
waitpid($pid, 0);
}
これは正常に機能しており、いくつかのことが明らかになりました。しかし、全体像はまだ少しぼんやりしています。
だから私の質問は:
- 何が問題なの
hung_execute
ですか? - open3呼び出しのおかげでうまくいくと思い
good_execute
ます。>&
しかし、なぜそしてどのように? - また、ファイルハンドルに(の代わりに)
good_execute
字句変数を使用した場合は機能しませんでした。このエラーが発生しました:。なぜそうなのか?my $out
OUT
open3: open(GLOB(0x610920), >&main::OUT) failed: Invalid argument
- 一度に書き込むことができるファイルハンドルは1つだけのようです。リソースを保持しているハンドルを破棄すると、他のハンドルは待機し続けます。以前は、STDERRとSTDOUTは独立したストリームであり、リソースを共有していないと考えていました。私の理解はここでは少し欠陥があると思います。これについてもいくつかの指針を教えてください。