我能否分别捕获标准输出和标准错误并保持原始顺序?

8

我使用本地win32 API编写了一个Windows应用程序。我的应用程序将启动其他进程并捕获输出,并在标准错误输出中突出显示红色。

为了实现这一点,我为stdout和stderr创建了单独的管道,并在调用CreateProcess时在STARTUPINFO结构中使用它们。然后,我为每个stdout / stderr句柄启动一个单独的线程,从管道中读取并将输出记录到窗口中。

在大多数情况下,这很好用。我遇到的问题是,如果子进程快速记录到stderr和stdout,我的应用程序有时会以不正确的顺序显示输出。我认为这是由于使用两个线程从每个句柄读取造成的。

是否可能按原始编写顺序捕获stdout和stderr,同时能够区分两者?

6个回答

3

我很确定除了编写被衍生程序以写入数据包并为每个数据包添加时间戳之外,无法做到这一点。如果没有这样的处理,通常可以预计缓冲会在子进程的标准库中发生,因此在通过管道传输到父进程时,它们很可能已经失序。


是的,这是正确的方法,但我不够勇敢告诉OP,因为在公开论坛中尝试这样做会导致太多的踩。 - Nicholas Jordan
这怎么可能是对的?CMD.exe 怎么总是能做对呢? - paulm
看起来是可以做到的,但无法知道写入哪个流:https://dev59.com/EWMl5IYBdhLWcg3wR1WD - paulm
@paulm:这可能对某些程序在某些情况下有效,但出于上述原因,它几乎肯定会在某些时候失败。 - Jerry Coffin
这是在CMD下始终有效的工作方式,如果相同的流用于两者,则顺序将保持不变。 - paulm

2
在我看到的大多数stdout和stderr实现中,stdout是有缓冲区的,而stderr则没有。基本上这意味着即使在直接命令行下运行程序,也不能保证它们的顺序是一致的。
简短的回答:你无法确保按照cmd.exe上出现的顺序读取行,因为它们在cmd.exe上出现的顺序并不是有保障的。
参考链接:http://en.wikipedia.org/wiki/Stderr#Standard_error_.28stderr.29

0

您可以将stderr重定向到stdout:

command_name 2>&1

我记得在C语言中可以使用管道实现这一点。

更新:哦,抱歉——错过了能够区分两者的部分。我知道TextMate以某种用户可见的代码方式实现了它...虽然有一段时间没有看了,但我会去看一下的。但经过进一步的思考,您是否可以在Ruby中使用Open3之类的东西?您必须同时观察STDOUTSTDERR,但实际上没有人应该期望关于这两个输出的特定排序。

更新2:我在Ruby中所说的示例:

require 'open3'

Open3.popen3('ruby print3.rb') do |stdin, stdout, stderr|
  loop do
    puts stdout.gets
    puts stderr.gets
  end
end

...其中print3.rb只是:

loop do
  $stdout.puts 'hello from stdout'
  $stderr.puts 'hello from stderr'
end

不要直接将输出传递给puts,而是可以向观察者发送消息,在程序中打印出来。抱歉,我在这台机器上没有Windows(或任何立即可用的),但我希望这说明了这个概念。


0

并不是这样,你可能会这么想,但 std_out 受系统设计师控制 - std_out 的编写方式和时间取决于系统调度程序,而根据我的测试,系统调度程序受到的问题并没有得到充分记录。

有一天我在编辑器中打开代码并在系统上的一个设备上进行了一些工作,发现系统正在为驱动程序提供实时优先级,使得我精心编写的 C 代码比专有代码重要性低了约十分之一。

将其重新反转以获得写入的顺序将是极具挑战性的,至少可以这么说。


0

我非常确定,即使您根本不将它们分开,也不能保证它们会以正确的顺序互换。


0

由于意图是注释现有程序的输出,因此两个流的任何可能交错都必须正确。原始开发人员将放置适当的flush()调用以确保遵守任何强制排序。

如先前所述,记录每个片段的时间戳,并使用它来恢复实际由输出设备看到的序列。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接