如何在不停止写入终端的情况下复制stderr?

Wil*_*hes 6 bash stderr

我想编写一个运行命令的shell脚本,在它到达时将其stderr写入终端.但是,我还想将stderr保存到变量中,以便稍后检查.

我怎样才能做到这一点?我应该使用tee,还是子壳,还是其他什么?

我试过这个:

# Create FD 3 that can be used so stdout still comes through 
exec 3>&1

# Run the command, piping stdout to normal stdout, but saving stderr.
{ ERROR=$( $@ 2>&1 1>&3) ; }

echo "copy of stderr: $ERROR"
Run Code Online (Sandbox Code Playgroud)

但是,这不会将stderr写入控制台,它只会保存它.

我也尝试过:

{ $@; } 2> >(tee stderr.txt >&2 )

echo "stderr was:"
cat stderr.txt
Run Code Online (Sandbox Code Playgroud)

但是,我不想要临时文件.

War*_*rbo 5

我经常想这样做,发现自己很热衷/dev/stderr,但是这种方法可能会出现问题。例如,如果Nix构建脚本尝试写入/dev/stdout或,则会给出“ permission否认”错误/dev/stderr

在几次重新设计轮子之后,我目前的方法是按如下方式使用流程替换:

myCmd 2> >(tee >(cat 1>&2))
Run Code Online (Sandbox Code Playgroud)

从外部阅读:

这将运行myCmd,保持其标准输出不变。在2>将重定向的标准错误myCmd到不同的目的地; 这里的目的地>(tee >(cat 1>&2))将导致将其通过管道传递到命令中tee >(cat 1>&2)

tee命令将其输入(在本例中为的stderr myCmd)复制到其stdout和给定的目的地。这里的目的地是>(cat 1>&2),这将导致数据通过管道传递到命令中cat 1>&2

cat命令仅将其输入直接传递到stdout。该1>&2重定向标准输出去到stderr。

由内而外的阅读:

cat 1>&2命令将其stdin重定向到stderr,因此>(cat 1>&2)行为类似于/dev/stderr

因此,tee >(cat 1>&2)将其stdin复制到stdout和stderr,就像一样tee /dev/stderr

我们2> >(tee >(cat 1>&2))曾经得到2份stderr副本:一份在stdout上,一份在stderr上。

我们可以正常使用stdout上的副本,例如将其存储在变量中。我们可以将副本留在stderr上以打印到终端上。

如果愿意,我们可以将其与其他重定向结合使用,例如

# Create FD 3 that can be used so stdout still comes through 
exec 3>&1

# Run the command, redirecting its stdout to the shell's stdout,
# duplicating its stderr and sending one copy to the shell's stderr
# and using the other to replace the command's stdout, which we then
# capture
{ ERROR=$( $@ 2> >(tee >(cat 1>&2)) 1>&3) ; }

echo "copy of stderr: $ERROR"
Run Code Online (Sandbox Code Playgroud)


mkl*_*nt0 4

该方法的基本原理归功于@Etan Reisner;但是,最好使用teewith/dev/stderr而不是/dev/tty为了保留正常行为(如果您发送到/dev/tty,外界不会将其视为 stderr 输出,并且既无法捕获也无法抑制它):

这是完整的习语:

exec 3>&1   # Save original stdout in temp. fd #3.
# Redirect stderr to *captured* stdout, send stdout to *saved* stdout, also send
# captured stdout (and thus stderr) to original stderr.
errOutput=$("$@" 2>&1 1>&3 | tee /dev/stderr)
exec 3>&-   # Close temp. fd.

echo "copy of stderr: $errOutput"    
Run Code Online (Sandbox Code Playgroud)