2009-10-20 12 views
6

私は外部ツール(cleartool)を使ってファイルのリストに関する情報を収集するPerlスクリプトを持っています。 IPCを使用して、各ファイルの新しいプロセスを生成しないようにします。WindowsでノンブロッキングIPC読み取りを実行するにはどうすればよいですか?

use IPC::Open2; 
my ($cin, $cout); 
my $child = open2($cout, $cin, 'cleartool'); 

単一行を返すコマンドはうまく機能します。例えば複数行を返す

print $cin "describe -short $file\n"; 
my $description = <$cout>; 

コマンドブロックでハングアップ取得せずに全体の応答を消費する方法については行き止まりで私を持っているが読んで:私はファイルハンドルを設定しようとした

print $cin "lshistory $file\n"; 
# read and process $cout... 

非ブロッキングがfcntl経由で読み込む:

use Fcntl; 
my $flags = ''; 
fcntl($cout, F_GETFL, $flags); 
$flags |= O_NONBLOCK; 
fcntl($cout, F_SETFL, $flags); 

けどをFcntlは、メッセージに死ぬ「あなたのベンダーが定義されていないをFcntlマクロF_GETFL。」

IO :: Handleを使って$cout->blocking(0)を設定しようとしましたが、失敗しました(undefを返し、$!を "不明なエラー"に設定します)。

私は読み取ろうとする前に、利用可能なデータがあるかどうかを決定するためにselectを使用しようとしました:

my $rfd = ''; 
vec($rfd, fileno($cout), 1) = 1; 
while (select($rfd, undef, undef, 0) >= 0) { 
    my $n = read($cout, $buffer, 1024); 
    print "Read $n bytes\n"; 
    # do something with $buffer... 
} 

が、それは今まで何も読まずにハングアップします。誰でもこの作業を(Windows上で)行う方法を知っていますか?

+0

IOでブロックしていない間に何をする予定ですか? – jrockway

+0

私はデータを取得するのを止めるまで読んでみたいと思います。これは、すでに取得したデータを解析して最終的に達成したと判断するよりも信頼性が高いようです。 (これは最後の行であることはデータにはありません) –

+0

関連する問題を調べると、GitHubでこのコミットが検出されました。これは非ブロックソケットを作るために黒い魔法を呼び起こすようです:https:// github .com/kthakore /フリーズバブル/コミット/ 735dd2307455e32c69827e013992c2d022cb7347 –

答えて

5

select only works on sockets Windows。 IPC :: OpenXは通常のファイルハンドルを使用しているため、作成するハンドルにselectを使用することはできません。

selectが提供するアクティビティのタイムアウト/検出が不要な場合は、ハンドルをノンブロッキングに設定し、通常通りに読み書きすることができます。

さらに微妙なコントロールが必要な場合は、IPC::Runがうまくいくかもしれません。

socketpairを作成し、それらのハンドルを子プロセスで使用することもできます。新しいPerl(5.8以降)は、TCPソケットを使用してWindows上でsocketpairエミュレーションをサポートしています。

STDOUTSTDERRをコンソールなしで実行するプログラム(つまり、perlではなくwperlで起動)に対してクローンを作成しようとすると、STDIOでデータを取得できません。

実際には、これはいくつかのプロジェクトで私にとって大きな苦痛でした。私が最もうまくいったのは、TCPを介して親サーバーに接続する子プロセスを作成することでした。子プロセスを制御しない場合は、IPC::Runまたはsocketpairを参照してください。

+1

うん、Windowsは吸う。そこで走っているとき、IPC :: Runはあなたが言及しているソケットトリックを行います。 – ephemient

+0

私が質問に書いたように、私はFcntlまたはIO :: Handleのどちらかを使ってハンドルを非ブロックにすることができませんでした。あなたがそれを行う方法を知っているなら(それはWindows上で動作します)、それを共有してください。 –

+0

@Michael:ブロックIOを別のプロセスで実行します。ソケットを使用してメインプロセスとやりとりします。子プロセスはブロックされるかもしれませんが、メインプロセスは通信ソケット上で非ブロックI/Oを実行できます。これはdaotoadが 'socketpair'で提案したものです。 – ephemient

0

ノンブロッキングIOは、Windowsではハードです。このケースでは、通常のファイルへのcleartoolの出力を送ることができ、そしてアウトするたびにファイルから読み込んだファイルにEOFフラグをリセットするためにseekを使用する:これはおそらくことを動作しませんが

my($cin, $cout); 
my $somefile = "some/file"; 
open($cin, "| cleartool > $somefile"); 
open($cout, '<', $somefile); 

... 

print $cin "$command_for_cleartool\n"; 
# if necessary, wait until cleartool finishes with new output 
seek $cout, 0, 1;  # clear eof condition from previous read 
my @cleartool_output = <$cout>; # capture recent output 
... process output ... 

もしcleartoolがその出力をバッファするならば

1

もう1つのkludgeは、大きいかほとんどないバッファサイズのsysreadを使用することです。

print $cin "$command_for_cleartool\n"; 
    my ($description, $maxlen, $buffer) = ("", 65336); 
    while (my $n = sysread $cout, $buffer, $maxlen) { 
     $description .= $buffer; 
     last if $n < $maxlen; 
    } 
    ... do something with $description ... 

sysread読まれるのを待っている入力の正確0バイトがある場合はハングします。したがって、cleartoolが65336バイトの倍数を正確に生成すると、上記のコードはハングします。プログラムの出力サイズの上限がわかっている場合は、上の$maxlenの値を使用できます。それ以外の場合は、大規模ではない数字を選んで祈ることができます。

関連する問題