有没有比下面的代码更好的方法来为命令的输出添加前缀,但仍然能够检查其返回值?
command > "$tmpfile" 2>&1
ret=$?
sed 's/^/ | /' "$tmpfile"
if [[ ! "$ret" -eq 0 ]]; then
rm "$tmpfile"
exit 1
fi
答案1
对于bash
其他一些 shell,您可以使用该pipefail
选项,启用该选项后会导致管道的退出状态为失败的最右侧组件的退出状态,而不是最右侧组件的退出状态:
(set -o pipefail; cmd | sed 's/^/ | /') || exit
另一种选择是将 stdout 重定向到进程替换而不是使用管道:
cmd > >(sed 's/^/ | /') || exit
尽管请注意,sed
在这种情况下,它会在后台启动并且不会等待,但在运行下一个命令时可能尚未完成写入。
具体到bash
,有一个$PIPESTATUS
特殊的数组(zsh 有一个$pipestatus
等效的),它记录管道中每个组件的退出状态:
cmd | sed 's/^/ | /'; ret=${PIPESTATUS[0]}
[ "$ret" -eq 0 ] || exit "$ret"
POSIXly,你可以这样做:
{
ret=$(
exec 4>&1
{
cmd 3>&- 4>&-
echo "$?" >&4
} | sed 's/^/ | /' >&3 3>&- 4>&-
)
} 3>&1
[ "$ret" -eq 0 ] || exit "$ret"
您可以使用辅助函数:
prefix() (
prefix=$1; shift
exit "$(
exec 4>&1
{
"$@" 3>&- 4>&-
echo "$?" >&4 3>&- 4>&-
} | PREFIX=$prefix awk '{print ENVIRON["PREFIX"] $0}' >&3 3>&- 4>&-
)"
) 3>&1
prefix ' | ' cmd
(这里切换到awk
以便能够使用任意前缀。