假设我有一个像下面这样的脚本:

useless.sh

echo "This Is Error" 1>&2
echo "This Is Output" 

我有另一个shell脚本:

alsoUseless.sh

./useless.sh | sed 's/Output/Useless/'

我想捕捉“这是错误”,或任何其他stderr从无用的。sh,到一个变量。 我们称它为ERROR。

注意,我使用了stdout。我想继续使用stdout,所以在这种情况下,将stderr重定向到stdout没有帮助。

所以,基本上,我想做

./useless.sh 2> $ERROR | ...

但这显然行不通。

我也知道我能做到

./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`

但这是丑陋和不必要的。

不幸的是,如果这里没有答案,这就是我要做的。

我希望还有别的办法。

有人有更好的主意吗?


当前回答

如果您想绕过临时文件的使用,可以使用进程替换。我还没把它弄好。这是我的第一次尝试:

$ .useless.sh 2> >( ERROR=$(<) )
-bash: command substitution: line 42: syntax error near unexpected token `)'
-bash: command substitution: line 42: `<)'

然后我尝试了

$ ./useless.sh 2> >( ERROR=$( cat <() )  )
This Is Output
$ echo $ERROR   # $ERROR is empty

然而

$ ./useless.sh 2> >( cat <() > asdf.txt )
This Is Output
$ cat asdf.txt
This Is Error

所以过程替代通常是正确的…不幸的是,每当我在>()内用$()中的东西包装STDIN以试图将其捕获到一个变量时,我就会丢失$()的内容。我认为这是因为$()启动了一个子进程,该子进程不再访问父进程所拥有的/dev/fd中的文件描述符。

进程替换为我购买了与不再在STDERR中的数据流工作的能力,不幸的是,我似乎无法以我想要的方式操纵它。

其他回答

这样捕获错误文件会更简洁:

ERROR=$(</tmp/Error)

shell可以识别这一点,并且不需要运行'cat'来获取数据。

更大的问题很难。我不认为有什么简单的方法可以做到。您必须将整个管道构建到子shell中,最终将其最终标准输出发送到一个文件,以便将错误重定向到标准输出。

ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )

注意,分号是必需的(在经典shell中,Bourne, Korn,这是肯定的;可能在Bash中也是如此)。'{}'对所包含的命令进行I/O重定向。如前所述,它也会从sed捕获错误。

警告:正式未经测试的代码-使用风险自负。

这是一个有趣的问题,我希望有一个优雅的解决方案。遗憾的是,我最终得到了一个类似于Leffler先生的解决方案,但我要补充的是,你可以在Bash函数中调用useless来提高可读性:

#!/bin/bash

function useless {
    /tmp/useless.sh | sed 's/Output/Useless/'
}

ERROR=$(useless)
echo $ERROR

所有其他类型的输出重定向必须由一个临时文件支持。

# command receives its input from stdin.
# command sends its output to stdout.
exec 3>&1
stderr="$(command </dev/stdin 2>&1 1>&3)"
exitcode="${?}"
echo "STDERR: $stderr"
exit ${exitcode}

我是这样做的:

#
# $1 - name of the (global) variable where the contents of stderr will be stored
# $2 - command to be executed
#
captureStderr()
{
    local tmpFile=$(mktemp)

    $2 2> $tmpFile

    eval "$1=$(< $tmpFile)"

    rm $tmpFile
}

使用示例:

captureStderr err "./useless.sh"

echo -$err-

它确实使用了一个临时文件。但至少丑陋的东西被包裹在一个函数中。

这篇文章帮助我想出了一个类似的解决方案:

MESSAGE=`{ echo $ERROR_MESSAGE | format_logs.py --level=ERROR; } 2>&1`

然后只要MESSAGE不是空字符串,我们就把它传递给其他东西。这将让我们知道我们的format_logs.py是否因某种python异常而失败。