Perl スクリプトからシステム コマンドの stdin と stdout をキャプチャするにはどうすればよいですか?
質問
Perl スクリプトの途中に、実行したいシステム コマンドがあります。標準入力に入力する必要があるデータを含む文字列があり (コマンドは標準入力からの入力のみを受け入れます)、標準出力に書き込まれた出力をキャプチャする必要があります。Perl でシステム コマンドを実行するさまざまな方法を調べてきました。 open
この関数は私が必要としているもののようですが、stdinまたはstdoutのみをキャプチャでき、両方はキャプチャできないようです。
現時点では、私の最良の解決策は次のとおりです。 open
, 、標準出力を一時ファイルにリダイレクトし、コマンドの終了後にファイルから読み取ります。もっと良い解決策はありますか?
解決
見てみたいと思います IPC::Open2
他のヒント
IPC::Open2/3 は問題ありませんが、通常、本当に必要なのは IPC::Run3, 、最小限の複雑さで単純なケースを非常にうまく処理します。
use IPC::Run3; # Exports run3() by default
run3( \@cmd, \$in, \$out, \$err );
ドキュメントでは、IPC::Run3 を他の代替手段と比較しています。使用することに決めていなくても、一読の価値はあります。
の perlipc ドキュメント IPC::Open2 や IPC::Open3 など、これを行うための多くの方法を説明しています。
スクリプトの先頭のどこかに、次の行を含めます。
use IPC::Open2;
これには、通常、ほとんどの Perl ディストリビューションにデフォルトでインストールされる必要なモジュールが含まれます。(お持ちでない場合は、CPAN を使用してインストールできます。) 次に、開く代わりに、次のように呼び出します。
$pid = open2($cmd_out, $cmd_in, 'some cmd and args');
$cmd_in にデータを送信してコマンドにデータを送信し、$cmd_out から読み取ることでコマンドの出力を読み取ることができます。
コマンドの stderr ストリームも読み取れるようにしたい場合は、代わりに IPC::Open3 モジュールを使用できます。
IPC::Open3 はおそらくあなたが望むことを行うでしょう。STDERR と STDOUT をキャプチャできます。
私が最近見つけたこれを行うための非常に簡単な方法は、 の IPC::フィルター モジュール. 。これにより、非常に直感的に作業を行うことができます。
$output = filter $input, 'somecmd', '--with', 'various=args', '--etc';
リストを渡すと、シェルを経由せずにコマンドがどのように呼び出されるかに注目してください。また、一般的なユーティリティのエラーを適切に処理します。(失敗すると、 die
s、STDERR からのテキストをエラー メッセージとして使用します。成功すると、STDERR は単に破棄されます。)
もちろん、ストリーミング処理を行う方法がないため、大量のデータには適していません。また、エラー処理の粒度がニーズに合わない場合もあります。しかし、実際には多くの単純なケースが発生します 本当に 単純。
特別な Perl コマンドがあります
open2()
詳細については、以下を参照してください。 http://sunsite.ualberta.ca/Documentation/Misc/perl-5.6.1/lib/IPC/Open2.html
追加のパッケージを含めたくない場合は、次のようにすることができます
open(TMP,">tmpfile");
print TMP $tmpdata ;
open(RES,"$yourcommand|");
$res = "" ;
while(<RES>){
$res .= $_ ;
}
これはあなたが提案したこととは逆ですが、これも機能するはずです。
単一行の出力のみを期待している場合、または結果を改行以外で分割したい場合は、私は常にこの方法を使用します。
my $result = qx( command args 2>&1 );
my $rc=$?;
# $rc >> 8 is the exit code of the called program.
if ($rc != 0 ) {
error();
}
複数行の応答を処理したい場合は、結果を配列として取得します。
my @lines = qx( command args 2>&1 );
foreach ( my $line ) (@lines) {
if ( $line =~ /some pattern/ ) {
do_something();
}
}