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

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`

但这是丑陋和不必要的。

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

我希望还有别的办法。

有人有更好的主意吗?


当前回答

我是这样做的:

#
# $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-

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

其他回答

重定向stderr到stdout, stdout到/dev/null,然后使用反勾号或$()捕获重定向的stderr:

ERROR=$(./useless.sh 2>&1 >/dev/null)

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

#!/bin/bash

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

ERROR=$(useless)
echo $ERROR

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

我是这样做的:

#
# $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异常而失败。

捕获并打印标准码

ERROR=$( ./useless.sh 3>&1 1>&2 2>&3 | tee /dev/fd/2 )

分解

您可以使用$()来捕获stdout,但您希望捕获stderr。你交换了stdout和stderr。在标准交换算法中使用fd 3作为临时存储。

如果你想捕捉和打印使用tee做一个副本。在这种情况下,tee的输出将被$()捕获,而不是转到控制台,但stderr(的tee)仍然会转到控制台,因此我们使用它作为tee的第二个输出,通过特殊文件/dev/fd/2,因为tee需要一个文件路径而不是fd数。

注意:这是一个可怕的多重定向在单行和顺序问题。$()在管道的末尾抓取tee的stdout,并且管道本身将./useless.sh的stdout路由到tee的stdin,在我们将stdin和stdout交换为./useless.sh之后。

使用./useless.sh的stdout

OP说他仍然想使用(不仅仅是打印)标准输出,比如。/Useless .sh | sed 's/Output/Useless/'。

没问题,只是在交换stdout和stderr之前这样做。我建议将它移动到一个函数或文件中(同样是useless.sh),并调用它来代替上面一行中的./useless.sh。

然而,如果你想要捕获标准输出和标准输出,那么我认为你必须回到临时文件,因为$()一次只会做一个,并且它会生成一个子shell,从中你不能返回变量。