web-dev-qa-db-ja.com

Perlでstderr、stdout、およびexitコードを一度にキャプチャするにはどうすればよいですか?

Perlから外部プロセスを実行し、そのstderr、stdout、およびプロセス終了コードをキャプチャすることは可能ですか?

私はこれらの組み合わせを行うことができるようです。バックティックを使用してstdoutを取得し、IPC :: Open3を使用して出力をキャプチャし、system()を使用して終了コードを取得します。

どのようにしてstderr、stdout、exitコードを一度にキャプチャしますか?

56
Scooby

IPC :: Open3のドキュメントを読み直すと、 waitpid を呼び出して子プロセスを取得する必要があるという注意が表示されます。これを行うと、ステータスが$?。終了値は$? >> 8。見る - $? in perldoc perlvar

26
Michael Carman

Update:IO :: CaptureOutputのAPIを更新して、これをさらに簡単にしました。)

これを行うにはいくつかの方法があります。 IO :: CaptureOutput モジュールを使用する1つのオプションを次に示します。

use IO::CaptureOutput qw/capture_exec/;

my ($stdout, $stderr, $success, $exit_code) = capture_exec( @cmd );

これはcapture_exec()関数ですが、IO :: CaptureOutputには、Perl出力または外部プログラムからの出力をキャプチャするために使用できる、より一般的なcapture()関数もあります。したがって、Perlモジュールが外部プログラムを使用した場合でも、出力が得られます。

また、外部プログラムやその他のPerl出力をキャプチャするモジュールにIPC :: Open3を使用する代わりに、STDOUTとSTDERRをキャプチャする(またはそれらをマージする)単一のアプローチを覚えるだけでよいことも意味します。

41
xdg

STDERRの内容が必要ない場合は、 IPC :: System :: Simple モジュールからのcapture()コマンドがほぼ正確です。

   use IPC::System::Simple qw(capture system $EXITVAL);

   my $output = capture($cmd, @args);

   my $exit_value = $EXITVAL;

1つの引数を指定してcapture()を使用してシェルを呼び出すか、複数の引数を指定してシェルを確実に回避できます。引数が1つであってもシェルを呼び出さないcapturex()もあります。

Perlの組み込みシステムおよびバックティックコマンドとは異なり、IPC :: System :: SimpleはWindowsで完全な32ビットの終了値を返します。また、コマンドを開始できない場合、シグナルで停止する場合、または予期しない終了値を返す場合、詳細な例外をスローします。つまり、多くのプログラムでは、終了値を自分でチェックするのではなく、IPC :: System :: Simpleを使用してハードワークを行うことができます。

 use IPC::System::Simple qw(system capture $EXIT_ANY);

 system( [0,1], "frobincate", @files);     # Must return exitval 0 or 1

 my @lines = capture($EXIT_ANY, "baznicate", @files);  # Any exitval is OK.

 foreach my $record (@lines) {
     system( [0, 32], "barnicate", $record);  # Must return exitval 0 or 32
 }

IPC :: System :: Simpleは純粋なPerlであり、依存関係はなく、UnixシステムとWindowsシステムの両方で動作します。残念ながら、STDERRをキャプチャする方法は提供されていないため、すべてのニーズに適しているとは限りません。

IPC :: Run は、3つの一般的なファイルハンドルすべてを再配管するためのクリーンで簡単なインターフェイスを提供しますが、残念ながらコマンドが成功したかどうかを確認しないため、$を検査する必要があります。手動では、まったく面白くありません。 $を検査するためのパブリックインターフェイスを提供しますか?私の to-doリスト にあるものは、IPC :: System :: Simpleで、$を調べているので、クロスプラットフォームでの作業は、私が誰にでも望んでいることではありません。

IPC :: 名前空間には、支援を提供する他のモジュールもあります。 YMMV。

ではごきげんよう、

ポール

14
pjf

外部コマンドを実行する3つの基本的な方法があります。

_system $cmd;        # using system()
$output = `$cmd`;       # using backticks (``)
open (PIPE, "cmd |");   # using open()
_

system()を使用すると、system()コマンドによってリダイレクトされない限り、STDOUTとSTDERRの両方がスクリプトのSTDOUTと_STDERR,_と同じ場所に移動します。 。バックティックとopen()は、コマンドのSTDOUTのみを読み取ります。

Openで次のようなものを呼び出して、STDOUTSTDERRの両方をリダイレクトすることもできます。

_open(PIPE, "cmd 2>&1 |");
_

@ Michael Carman で示されるように、戻りコードは常に_$?_に格納されます。

7
hoyhoy

本当に複雑になっている場合は、Expect.pmを試してみてください。ただし、プロセスへの入力の送信も管理する必要がない場合は、おそらくやり過ぎです。

0
skiphoppy

IPC:run が非常に役立つことがわかりました。すべての子パイプをグロブまたは変数に転送できます。とても簡単に!終了コードは$?に保存されます。

以下は、私が知っているstderrを数字で取得する方法です。 cmdは、stdoutへの情報変換(>を使用してargsのファイルにパイプしました)を出力し、STDERRへの変換の数を報告しました。

use IPC::Run3

my $number;
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number);
die "Command failed: $!" unless ($run && $? == 0);
0
Ian