Bash - 获取 return 代码、stdout 和 stderr 形式的管道调用

Bash - getting return code, stdout and stderr form piped invocations

我做了一个简单的记录器,它有方法logMETHOD。它的工作是

  1. stderrstdout 放入变量 log(然后放入我的全局 _LOG 变量)
  2. stderr 上打印 stderr 并在 stdout 上打印 stdout 以便我可以在控制台中看到它。
  3. Return 被调用函数的 return 代码。

它的调用看起来像这样:

logMETHOD myMethod arg1 arg2 arg3

我想出了如何将标准和错误输出同时输出到 log 变量和控制台,但我无法获得正确的 return 代码。

到目前为止我的代码:

function logMETHOD {
    exec 5>&1
    local log
    log="$( ${@:2} 2>&1 | tee /dev/fd/5)"
    local retVal=$?
    _LOG+=$log$'\n'
    return $retVal
}

不幸的是,我得到的 return 代码来自(可能)赋值(或者可能来自 tee)。

奖金问题: 如果没有 2>&1 连接 stdoutstderr 也用于控制台,是否有可能实现我的目标?

我用 'PIPESTATUS' 测试了解决方案,但代码仍然是 0。

function main {
    logMETHOD alwaysError
}

function logMETHOD {
    exec 5>&1
    local log
    local retVal
    log="$( "$@" 2>&1 | tee /dev/fd/5 )"
    retVal=${PIPESTATUS[0]}
    echo "RETVAL: $retVal"
    echo "LOG: $log"
    _LOG+=$log$'\n'
    return $retVal
}

function alwaysError {
    return 1
}

main $@

最简单的修复方法是使用 PIPESTATUS 检索命令的退出状态。

# There's no need to split  off of $@ to run the command.
log=$( "$@" 2>&1 | tee /dev/fd/5 )
local retVal=${PIPESTATUS[0]}

PIPESTATUS 将是一个很好的解决方案,但这里它已经包含 log=... 赋值的 return 值。如果你想要 "$@"... 的 return 值,你必须这样写:

log="$( "$@" 2>&1 | tee /dev/fd/5; echo ${PIPESTATUS[0]}>/tmp/retval )"
retVal=$(</tmp/retval)

将它分配给一个变量是行不通的,因为它的范围不会扩展到调用 shell,所以你必须求助于使用临时文件。

至于stderr$()只能提取stdout,所以你也必须使用临时文件,如果你想单独处理它。

log="$( "$@" 2>/tmp/stderr | tee /dev/fd/5; echo ${PIPESTATUS[0]}>/tmp/retval )"
stderr_log=$(</tmp/stderr)
retVal=$(</tmp/retval)

如果您只想避免重定向:

log="$( "$@" |& tee /dev/fd/5; echo ${PIPESTATUS[0]}>/tmp/retval )"

来自man bash

If |& is used, command's standard error, in addition to its standard output, is connected to command2's standard input through the pipe; it is shorthand for 2>&1 |. This implicit redirection of the standard error to the standard output is performed after any redirections specified by the command.