我想编写一个运行命令的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)
但是,我不想要临时文件.
我经常想这样做,发现自己很热衷/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)
该方法的基本原理归功于@Etan Reisner;但是,最好使用tee
with/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)