以前のコマンド(tar)も失敗した場合にawkが失敗するようにするにはどうすればよいですか?

以前のコマンド(tar)も失敗した場合にawkが失敗するようにするにはどうすればよいですか?

tarファイルを抽出し、1秒あたりに抽出されたファイル数を印刷する次のコマンドには、いくつかの問題があります。

tar -xvf some_tar.tar -C a/directory | awk 'systime() > lasttime { lasttime = systime(); printf "%d files\n", NR; fflush(stdout) }'

tarコマンドが失敗しても、awkコマンドはまだ0を返しますが、これはtarコマンドの失敗を反映していないため、望ましくありません。

この問題をどのように解決できますか?

答え1

pipefailパイプラインのコマンドが失敗したことを確認するには、このオプションを設定します。 Bashに加えて、ksh、zsh、およびBusybox(少なくとも)もこれをサポートします。このオプションが設定されている場合、パイプの終了状態は、関連コマンドによって返される最も左側のゼロ以外の終了状態です。

$ set -o pipefail
$ (exit 123) | true
$ echo $?
123

あるいは、パイプは条件付きです(「失敗しました」とマークする必要があります)。

set -o pipefail
if false | true; then
    echo it succeeded
else
    echo it failed
fi

答え2

最新バージョンの1つのアプローチは、bashコマンドパイプラインを呼び出した後に配列変数の値を確認することです。 Bashのマニュアルページによると:PIPESTATUStar|awk

PIPESTATUS
   An array variable (see Arrays below) containing a list of exit status
   values from the processes  in  the  most-recently-executed foreground
   pipeline (which may contain only a single command).

したがって、tarの終了コードはにあり、${PIPESTATUS[0]}awkの終了コードはにあります${PIPESTATUS[1]}

答え3

パイプはまったく使用されません。名前付きパイプを使用してください。

mkfifo p
awk '...' < p &
tar -xvf some_tar.tar -C a/directory > p
echo $?

このawkコマンドはバックグラウンドで実行され、tar名前付きパイプへの書き込みが開始されるまでブロックされます。tarパイプの終わりを終了して閉じると、最後awkから残りの内容を読み取って終了します。このコマンドは代わりに終了ステータスをecho報告します。tarawk

答え4

各入力ラインを呼び出すと、パイプラインの速度が大幅に遅くなると予想されるため、パイプラインのsystime()出力は1秒あたりのtar抽出ファイル数を正確に反映しません。進行状況インジケータを表示するには、systime()1000 万行または 100 万入力の入力行ごとに 1 回だけ呼び出すことを検討するか、まったく呼び出すことなく、100 万行または 100 万入力行ごとに印刷することを検討してください。 1秒あたりのファイルをインポートしていますsystime()

要求された問題を解決し、呼び出しのオーバーヘッドをほぼ完全に排除するには、次の操作を実行することを検討してくださいsystime()(GNU awkを時間関数として使用し、最後に読み込んだ行を$0含めEND、入力にNULを含めることができます)。

{ tar -xvf some_tar.tar -C a/directory && printf '\0\n'; } |
awk -v n=1000000 '
    BEGIN { beg = systime() }
    NR%n == 0 { printf "%d files processed\n", NR }
    END {
        end = systime()
        if ( $0 == "\0" ) {
            numFiles = NR - 1
            exitStatus = 0
        }
        else {
            numFiles = NR
            exitStatus = 1
        }
        printf "%d files per sec\n", numFiles / (end > beg ? end - beg : 1)
        exit exitStatus
    }
'

関連情報