tcl exec先读stdout然后stderr?

X.M*_*.M. 4 stdout tcl exec stderr

我发现tcl exec命令首先从stdout返回字符串然后是stderr.例如,我的以下"测试脚本"按此顺序生成消息:

puts "test started"
puts stderr "some non-fatal error goes to stderr"
puts "test passed"
Run Code Online (Sandbox Code Playgroud)

然后我像这样执行脚本:

set ret [ catch { exec sh -c $cmd } msg ]
Run Code Online (Sandbox Code Playgroud)

而我从$ msg得到的是:

test started
test passed
some non-fatal error goes to stderr
Run Code Online (Sandbox Code Playgroud)

这真的让我很难得到正确的结果.

有人可以知道是否可以按顺序从stdout和stderr获取消息,并且:

1)请不要像这样重定向,这样可以确保它们全部按顺序排列:

set ret [ catch {exec $cmd >&log.txt} msg ]
Run Code Online (Sandbox Code Playgroud)

2)我必须在我的 tcl脚本中调用那个 tcl脚本,抱歉

3)我也不能直接获取.tcl测试脚本,因为在两者之间调用了其他脚本,如果我的tcl脚本只是源于那个 tcl脚本它就不会工作.

我使用的是tclsh 8.3

不确定这是否要求太多.我希望有人可以解决这个问题.谢谢.

Don*_*ows 5

首先,让我们为测试目的定义一个简单的命令,我们可以肯定会测试我们需要的东西:

set cmd "echo a; echo b >&2; echo c"
Run Code Online (Sandbox Code Playgroud)

接下来,我们使用一些额外的帮助来处理stdout和stderr流的合并(为了清楚起见,将命令分成几行,以便我们可以看到catch包装器的位置和包装的exec位置):

set ret [catch {
   exec sh -c $cmd |& cat
} msg]
Run Code Online (Sandbox Code Playgroud)

如果我们测试一下,我们会发现我们$ret已经存在0,并且$msg正确地排序:

a
b
c
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?诀窍是|&,在管道到另一个进程时进行合并.(我们之所以使用cat它是因为它只是通过而不会干扰.)

如果您使用的Tcl 8.6(测试版),您可以使用chan pipe来产生可以重定向通道stdout,并stderr2>@ fileId形式,但不是那么对你有用.(你知道8.3已经过时了吗?甚至8.4不再真正支持; 8.5是推荐的生产级代码.)