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

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`

但这是丑陋和不必要的。

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

我希望还有别的办法。

有人有更好的主意吗?


当前回答

捕获并打印标准码

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,从中你不能返回变量。

其他回答

在zsh:

{ . ./useless.sh > /dev/tty } 2>&1 | read ERROR
$ echo $ERROR
( your message )

alsoUseless.sh

这将允许您通过sed等命令来输出无用的.sh脚本的输出,并将stderr保存在名为error的变量中。管道的结果被发送到stdout以供显示或传输到另一个命令中。

它设置了两个额外的文件描述符来管理执行此操作所需的重定向。

#!/bin/bash

exec 3>&1 4>&2 #set up extra file descriptors

error=$( { ./useless.sh | sed 's/Output/Useless/' 2>&4 1>&3; } 2>&1 )

echo "The message is \"${error}.\""

exec 3>&- 4>&- # release the extra file descriptors

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

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

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

ERROR=$(</tmp/Error)

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

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

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

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

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

$ b=$( ( a=$( (echo stdout;echo stderr >&2) ) ) 2>&1 )
$ echo "a=>$a b=>$b"
a=>stdout b=>stderr