@ikegamiからのガイダンスのおかげで、Perlで別のプロセスをインタラクティブに読み書きするための最良の選択はIPC::Runであることがわかりました。ただし、STDOUTへの書き込みが完了すると、プロンプトなど、読み取りおよび書き込みを行うプログラムが既知の出力を持つ必要があります。bash
を実行し、実行してからls -l
、その出力を出力する例を次に示します。
use v5.14;
use IPC::Run qw(start timeout new_appender new_chunker);
my @command = qw(bash);
# Connect to the other program.
my ($in, @out);
my $ipc = start \@command,
'<' => new_appender("echo __END__\n"), \$in,
'>' => new_chunker, sub { push @out, @_ },
timeout(10) or die "Error: $?\n";
# Send it a command and wait until it has received it.
$in .= "ls -l\n";
$ipc->pump while length $in;
# Wait until our end-of-output string appears.
$ipc->pump until @out && @out[-1] =~ /__END__\n/m;
pop @out;
say @out;
IPCとして実行されているため(私が推測します)、bash
STDOUTへの書き込みが完了してもプロンプトは表示されません。そのため、この関数を使用してnew_appender()
、出力の終わりを見つけるために一致させることができるものを出力します(を呼び出すことによってecho __END__
)。また、呼び出し後に匿名サブルーチンを使用してnew_chunker
、出力をスカラーではなく配列に収集しました(必要に'>'
応じて、スカラーへの参照を渡すだけです)。
したがって、これは機能しますが、私の意見では、さまざまな理由で問題が発生します。
- IPC制御のプログラムがSTDOUTに印刷されていることを知るための一般的に有用な方法はありません。代わりに、出力で正規表現を使用して、通常は完了したことを意味する文字列を検索する必要があります。
- それが1つを放出しない場合は、それをだまして1つを放出させる必要があります(ただし、ここで行ったように、名前の付いたファイルが必要な場合は神は禁じられて
__END__
います)。データベースクライアントを制御している場合は、のようなものを送信する必要があるかもしれませんSELECT 'IM OUTTA HERE';
。アプリケーションが異なれば、必要なnew_appender
ハックも異なります。
- 魔法
$in
と$out
スカラーへの書き込みは、奇妙で遠隔作用を感じます。嫌いです。
- スカラーがファイルハンドルである場合のように、スカラーに対して行指向の処理を行うことはできません。したがって、効率が低下します。
new_chunker
それでも少し奇妙な場合でも、行指向の出力を取得するために使用する機能は素晴らしいです。ただし、IPC :: Runによって効率的にバッファリングされると仮定すると、プログラムからの出力の読み取り効率が少し回復します。
私は今、IPC :: Runのインターフェースは少し良いかもしれませんが、特にIPCモデルの全体的な弱点により、対処するのが難しいことに気づきました。実行されている特定のプログラムの詳細を理解しすぎて機能させる必要があるため、一般的に有用なIPCインターフェイスはありません。これは、入力にどのように反応するかを正確に知っていて、出力の出力が完了したことを確実に認識でき、クロスプラットフォームの互換性についてあまり心配する必要がない場合は、おそらく問題ありません。しかし、オペレーティングシステムのホスト全体に配布できるCPANモジュールでさまざまなデータベースコマンドラインクライアントと対話するための一般的に便利な方法が必要な場合は、それだけでは十分ではありませんでした。
結局、ブログ投稿のコメントにパッケージの提案があったおかげで、私はそれらのクライアントを制御するためのIPCの使用をやめ、代わりにDBIを使用することにしました。優れたAPIを提供し、堅牢で安定しており、成熟しており、IPCの欠点はありません。
私の後に来る人への私の推薦はこれです:
- 別のプログラムを実行して終了するのを待つ必要がある場合、または実行が完了したときにその出力を収集する必要がある場合は、IPC :: System::Simpleを使用します。それ以外の場合、他の何かとインタラクティブにインターフェースする必要がある場合は、可能な限りAPIを使用してください。それが不可能な場合は、IPC :: Runのようなものを使用して、それを最大限に活用してみてください。そして、それを「適切に」取得するためにかなりの時間をあきらめる準備をしてください。