2

次のようなコマンドで実行されるperlスクリプトがあります。

/path/to/binary/executable | /path/to/perl/script.pl

スクリプトは、バイナリファイルの出力に対して有用な処理を実行し、STDINがなくなると終了します(<>はundefを返します)。バイナリがゼロ以外のコードで終了する場合を除いて、これはすべてうまくいきます。スクリプトのPOVから、スクリプトは正常に終了したと見なされるため、コード0でクリーンアップして終了します。

Perlスクリプトが終了コードを確認する方法はありますか?理想的には、次のような機能が必要です。

# close STDIN, and if there was an error, exit with that same error.
unless (close STDIN) {
   print "error closing STDIN: $! ($?)\n";
   exit $?;
}

しかし、残念ながら、これは機能していないようです。

$ (date; sleep 3; date; exit 1) | /path/to/perl/script.pl /tmp/test.out
Mon Jun  7 14:43:49 PDT 2010
Mon Jun  7 14:43:52 PDT 2010
$ echo $?
0

それを私が意味することをさせる方法はありますか?

追加するために編集:

perlスクリプトは、バイナリコマンドの出力をリアルタイムで操作しているため、すべてをファイルにバッファリングすることは実行可能な解決策ではありません。ただし、スクリプトが終了するまで、終了コードを知る必要はありません。

4

5 に答える 5

8

bash環境変数$PIPESTATUSは、最後のコマンドのパイプラインの各部分のステータスを含む配列です。例えば:

$ false | true; echo "PIPESTATUS: ${PIPESTATUS[@]};  ?: $?"
PIPESTATUS: 1 0;  ?: 0

したがって、perlスクリプトをリファクタリングするのではなく、そのパイプコマンドを実行するスクリプトでチェックする必要があるように思えます$PIPESTATUS$PIPESTATUSなしで使用する[@]と、配列の最初の要素の値が得られます。

初期実行可能ファイルとperlスクリプトの両方のステータスを確認する必要がある場合は、最初に$PIPESTATUSを別の変数に割り当てます。

status=(${PIPESTATUS[@]})

次に、次のように個別に確認できます。

if (( ${status[0]} )); then echo "main reactor core breach!"; exit 1;
elif (( ${status[1]} )); then echo "perls poisoned by toxic spill!"; exit 2;
fi;

次のステートメントは、ステートメントであっても、次のステートメントがステートメントであってもチェックできるようになる前にifリセットされるため、一時変数を介してこれを行う必要があります。${PIPESTATUS[@]}elif

これはbashでのみ機能し、元のBourneシェルでは機能しないことに注意してください(通常、多くのシステムは下位互換性のためにshリンク/bin/shしています)。/bin/bashしたがって、これをシェルスクリプトに入れると、最初の行は次のようになります。

#!/bin/bash

ではなく#!/bin/sh

于 2010-06-07T23:06:12.160 に答える
4

Etherの提案を詳しく説明すると、これはシェルの回避策のアプローチです。

bash-2.03$ TF=/tmp/rc_$$; (/bin/false; echo $?>$TF) | 
           perl5.8 -e 'print "OUT\n"'; test `cat $TF|tr -d "\012"` -eq 0 
OUT
bash-2.03$ echo $?
1
bash-2.03$ TF=/tmp/rc_$$; (/bin/true; echo $?>$TF) | 
           perl5.8 -e 'print "OUT\n"'; test `cat $TF|tr -d "\012"` -eq 0     
OUT
bash-2.03$ echo $?
0
bash-2.03$ 

欠点:

  • 一般的な醜さ

  • / tmp /rc_*ファイルの混乱を残します

  • ゼロ以外の終了コードの正確な値を失います(上記の例では255でした)

Perlスクリプトを次のようにわずかに編集することで、これらすべての欠点に対処できます。

  • $ENV{TF}(を使用して)という名前のファイルの内容を読み込み、次のFile::Slurp::read_file()ように言いますmy $rc
  • chomp $rc;
  • リンクを解除する$ENV{TF}
  • exit $rc
  • 次に、コマンドラインは次のようになります。 TF=/tmp/rc_$$; (/bin/true; echo $?>$TF) | /your/perl/script

これは、呼び出しを使用するためのEtherのスクリプトの変更と比較して、少し侵襲性の低い変更です。system()スクリプトの最後に4行を追加するだけです(含むexit)。しかし、とにかくスクリプトを変更したら、そもそも、Etherが提案する変更をすべて行って実行することをお勧めします。

于 2010-06-07T22:22:02.140 に答える
3

2つのオプションがあります。

  • スクリプトを書き直して、コマンド自体を呼び出すようにすることができます。これにより、スクリプトが終了ステータスを検出し、正常に終了しなかった場合に別のアクションを実行できるようになります。
  • コマンドの呼び出しをシェルスクリプトでラップすることができます。シェルスクリプトは、終了値をチェックしてから、Perlスクリプトを別の方法で呼び出します(Perlスクリプトを変更する必要がないことを除いて、基本的にオプション1と同じです)。

ただし、コマンドが終了する前にコマンドからPerlスクリプトの入力を読み取っているため、明らかにリターンコードはまだありません。コマンドが終了するとアクセスできるようになるので、その間にファイルなどの別の場所に出力をバッファリングする必要があります。

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

my $tempfile = File::Temp->new->filename;
system("/path/to/binary/executable > $tempfile");
if ($EXITVAL == 0)
{
     system("/path/to/perl/script.pl < $tempfile");
}
else
{
     die "oh noes!";
}
于 2010-06-07T22:04:13.833 に答える
2

自分の子プロセスの終了ステータスのみを取得します。STDINに関連するのは、perlの子プロセスではありません。それはシェルのものです。悲しいことに、あなたが望むことは不可能です。

于 2010-06-07T21:51:54.950 に答える
1

残念ながら、bashはパイプの終了ステータスを破棄しているようです。「sleep3|echo hi」を実行すると、スリープが完了する前にエコーが開始されるため、最初のコマンドの終了ステータスをキャプチャする機会はまったくありません。

(理論的には)bashコマンドをコマンドのリストに変更することでこれを実行できます-bashは値を$内に保存しますか?(Perlと同じように)しかし、それを何らかの方法でPerlスクリプトに渡す必要があります。つまり、Perlスクリプトは、(たとえば)コマンドラインで前のプログラムの終了ステータスを受け入れる必要があります。

または、Perlスクリプトを書き直してコマンドを実行し、終了ステータスをキャプチャするか、すべてをさらに別のスクリプトでラップすることもできます。

于 2010-06-07T22:20:27.243 に答える