16

アプリの実行中に別のプログラムの STDIN に定期的に書き込むことができる、別のプログラムに開かれたパイプを表す単純な IO オブジェクトを作成したいと考えています。防弾 (すべてのエラーをキャッチするという点で) であり、クロスプラットフォームであることを望んでいます。私が見つけることができる最良のオプションは次のとおりです。

open

sub io_read {
    local $SIG{__WARN__} = sub { }; # Silence warning.
    open my $pipe, '|-', @_ or die "Cannot exec $_[0]: $!\n";
    return $pipe;
}

利点:

  • クロスプラットフォーム
  • 単純

短所

  • $SIG{PIPE}パイプされたプログラムからエラーをキャッチする場合はいいえ
  • 他のエラーはキャッチされていますか?

IO::パイプ

sub io_read {
    IO::Pipe->reader(@_);
}

利点:

  • 単純
  • OO インターフェイスの IO::Handle オブジェクトを返します
  • Perl コアでサポートされています。

短所

  • $SIG{PIPE}パイプされたプログラムからエラーをキャッチするにはまだいいえ
  • Win32 ではサポートされていません (または、少なくともそのテストはスキップされます)

IPC::実行

IPC::Run にはファイル ハンドルに書き込むためのインターフェイスはなく、スカラーに追加するだけです。これは…奇妙に思えます。

IPC::Run3

ここにもファイルハンドルインターフェースはありません。子にスプールするために繰り返し呼び出されるコード参照を使用できますが、ソース コードを見ると、実際には一時ファイルに書き込み、それを開いてその内容をパイプ コマンドのSTDIN. なに?

IPC::コマンド

まだファイル ハンドル インターフェイスはありません。


ここで何が欠けていますか?これは解決された問題のように思えますが、そうではないことにちょっと驚いています。IO::Pipe は私が望んでいるものに最も近いものですが、$SIG{PIPE}エラー処理の欠如と Windows のサポートの欠如は悲惨です。JDWIM を実行するパイピング モジュールはどこにありますか?

4

2 に答える 2

6

@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として実行されているため(私が推測します)、bashSTDOUTへの書き込みが完了してもプロンプトは表示されません。そのため、この関数を使用して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のようなものを使用して、それを最大限に活用してみてください。そして、それを「適切に」取得するためにかなりの時間をあきらめる準備をしてください。
于 2012-05-22T05:07:02.540 に答える
0

私はこれに似たようなことをしました。親プログラムとパイプしようとしているものによって異なりますが。私の解決策は、子プロセスを生成し ($SIG{PIPE} を機能させたままにする)、それをログに書き込むか、適切と思われる方法でエラーを処理することでした。私は POSIX を使用して子プロセスを処理し、親プロセスのすべての機能を利用できます。ただし、子に親との通信を戻させようとしている場合は、状況が難しくなります。メインプログラムの例と、パイプしようとしているものはありますか?

于 2012-05-13T07:01:31.423 に答える