我对以下命令有一些问题,该命令提取 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 手册页:PIPESTATUS
tar|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
}
'