按时间顺序捕获STDOUT和STDERR

时间:2018-05-21 16:04:27

标签: linux unix sh tee

这很可能属于KISS(保持简单)的原则,但我仍然很好奇并希望接受教育,为什么我没有收到预期的结果。所以,我们在这里......

我有一个shell脚本来捕获STDOUT和STDERR而不会干扰原始文件描述符。这是为了保留终端上用户看到的原始输出顺序(参见下面的test.pl)。

不幸的是,我只能使用sh而不是bash(但我欢迎例子),因为我从另一个套件中调用它,我可能希望将来在cron中使用它(我知道cron有{ {1}}环境变量)。

SHELL包含:

wrapper.sh

#!/bin/sh stdout_and_stderr=$1 shift command=$@ out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$" mkfifo ${out} ${err} trap 'rm ${out} ${err}' EXIT > ${stdout_and_stderr} tee -a ${stdout_and_stderr} < ${out} & tee -a ${stdout_and_stderr} < ${err} >&2 & ${command} >${out} 2>${err} 包含:

test.pl

在场景中:

#!/usr/bin/perl

print "1: stdout1\n";
print STDERR "2: stderr1\n";
print "3: stdout2\n";

STDOUT包含:

sh wrapper.sh /tmp/xxx perl test.pl

STDERR包含:

1: stdout1
3: stdout2

到目前为止一切都很好......

2: stderr1 包含:

/tmp/xxx

但是,我期待2: stderr1 1: stdout1 3: stdout2 包含:

/tmp/xxx

有人可以向我解释为什么STDOUT和STDERR没有按照我预期的顺序追加1: stdout1 2: stderr1 3: stdout2 吗?我的猜测是,后台/tmp/xxx进程阻止了tee资源,因为它们具有相同的&#34;目的地&#34;。你会如何解决这个问题?

相关:How do I write stderr to a file while using "tee" with a pipe?

2 个答案:

答案 0 :(得分:2)

这是C运行时库的一个特性(可能被其他运行时库模仿)stderr未被缓冲。一旦写入,stderr就会将其所有字符推送到目标设备。

默认情况下stdout有一个512字节的缓冲区。

stderrstdout的缓冲可以通过setbufsetvbuf来电更改。

来自stdout的Linux手册页:

  

注意:流stderr是无缓冲的。当流stdout指向终端时,它是行缓冲的。在调用fflush(3)或exit(3)或打印换行符之前,不会出现部分行。这可能会产生意外结果,尤其是调试输出时。可以使用setbuf(3)或setvbuf(3)调用来更改标准流(或任何其他流)的缓冲模式。注意,在stdin与终端相关联的情况下,终端驱动程序中也可能存在输入缓冲,与stdio缓冲完全无关。 (实际上,通常终端输入在内核中是行缓冲的。)这个内核输入处理可以使用像tcsetattr(3)这样的调用来修改。另见stty(1)和termios(3)。

答案 1 :(得分:0)

经过@wallyk的灵感搜索后,我对wrapper.sh进行了以下修改:

#!/bin/sh
stdout_and_stderr=$1
shift
command=$@

out="${TMPDIR:-/tmp}/out.$$"
err="${TMPDIR:-/tmp}/err.$$"
mkfifo ${out} ${err}
trap 'rm ${out} ${err}' EXIT
> ${stdout_and_stderr}
tee -a ${stdout_and_stderr} < ${out} &
tee -a ${stdout_and_stderr} < ${err} >&2 &
script -q -F 2 ${command} >${out} 2>${err}

现在产生预期:

1: stdout1
2: stderr1
3: stdout2

解决方案是在$command前加script -q -F 2,使script完全(-q),然后强制文件描述符2(STDOUT)立即刷新({{ 1}})。

我正在研究确定这是多么便携。我认为-F 2可能是Mac和FreeBSD,而-F pipe-f可能是其他发行版......

相关:How to make output of any shell command unbuffered?