如果上一个命令 (tar) 也失败,如何导致 awk 失败?

如果上一个命令 (tar) 也失败,如何导致 awk 失败?

我对以下命令有一些问题,该命令提取 tar 文件并打印每秒提取的文件数量:

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

最近版本中的一种方法是在调用命令管道后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命令将报告tar的退出状态,而不是awk的。

答案4

我预计调用systime()每个输入行都会显着减慢管道速度,因此管道的输出将无法准确反映 tar 每秒提取的文件数量。如果您想查看进度指示器,请考虑仅systime()每千或百万或某些输入行调用一次,或者根本不调用并只打印每千或百万输入行,因为您并没有真正打印每秒如何提取文件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
    }
'

相关内容