为命令输出添加前缀并检查其返回值的最佳方法

为命令输出添加前缀并检查其返回值的最佳方法

有没有比下面的代码更好的方法来为命令的输出添加前缀,但仍然能够检查其返回值?

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以便能够使用任意前缀。

相关内容