4

Pls, imagine that I have some program which has output in both main streams: err && out. As an example we can use next bat file:

@echo off
echo 1
echo 2 >&2
echo 3
echo 4 >&2
echo 5
echo 6 >&2
echo 7
echo 8 >&2
echo 9
echo 10 >&2

so on the console' screen I will see this output:

1
2
3
4
5
6
7
8
9
10

And I want to see this output! Exactly as I expected. But also I want to see whole err stream simultaneously at some specific 'ERR.out' file. So It's content taking into account my initial bat file should be:

2
4
6
8
10

So My question is - HOW to do that trick for windows XP SP3 in BAT file's scenario?

I would like to use next pseudo code - but it will not work of course. But I hope that the main logic will be the same: command.exe | print_it_as_is_on_screen | redirect_2_stream_to_ERR.out_file | send_whole_output_to_another_command.exe

4

1 回答 1

5

我相信不可能可靠地做你想做的事。下面是我的原始答案,它不起作用,然后是一个可能起作用的修改,但不能保证在所有情况下都有效,然后解释为什么它不能完美地完成。

有两个部分可以解决您的问题。

首先,您需要一个可以读取标准输入并将其写入标准输出和文件的 tee 程序。您可以使用 gnuwin32 CoreUtils for Windows中的 tee.exe ,也可以使用以下混合 JScript/批处理文件 - TEE.BAT

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

::--- Batch section within JScript comment that calls the internal JScript ----
@echo off
cscript //E:JScript //nologo "%~f0" %*
exit /b

----- End of JScript comment, beginning of normal JScript  ------------------*/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mode=2;
if (WScript.Arguments.Count()==2) {mode=8;}
var out = fso.OpenTextFile(WScript.Arguments(0),mode,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
  chr=WScript.StdIn.Read(1);
  WScript.StdOut.Write(chr);
  out.Write(chr);
}

确保 tee.exe 或 tee.bat 在您当前的文件夹中,或者在您的路径中的某个位置。

然后,您可以将批处理脚本的输出通过管道传输到 tee,如下所示:

yourBatch | tee output.txt

但是管道操作捕获标准输出并将其重定向到第二个程序。因此,上面的行将捕获 output.txt 中的标准输出,但您想捕获 stderr。

其次,您需要一种交换标准输出和标准错误的机制。这非常容易:-)

yourBatch 3>&2 2>&1 1>&3 | tee err.txt

我在我接受的答案中描述了上述内容的工作原理是否有办法仅将 stderr 重定向到 stdout(而不是将两者结合起来),以便可以将其通过管道传输到其他程序?

以上不起作用:-(

有多个时序问题会阻止上述内容正常工作。第一个问题是在 tee 准备好处理输入之前有一个启动时间。批处理文件(或任何进程)可以将交错的消息写入标准输出和标准错误,标准错误通过管道传输到 tee,标准输出直接到控制台。标准输出立即转发到控制台,但标准错误在 tee 准备输入和输出流时被延迟。在您的测试用例中,整个标准输出内容在 tee 甚至开始写入控制台之前就被写入控制台。所以控制台输出看起来像

1
3
5
7
9
2
4
6
8
10

通过使用额外的批处理脚本延迟启动程序直到 tee 完成初始化,我能够在 Win 7 和 XP 虚拟机上获得正确的输出。这仅适用于 gnu tee.exe。它不能与 tee.bat 一起可靠地工作。

首先,我在与测试文件夹相同的文件夹中创建 delay.bat。

@echo off
ping 192.0.2.2 -n 1 -w 1000 >nul
%*

然后下面的命令在我的机器上给出了正确的结果

delay.bat test.bat 3>&2 2>&1 1>&3 | tee.exe err.txt

如果我用 tee.bat 代替 tee.exe,结果将无法重现。即使使用 tee.exe,它也可能无法在其他机器上运行。

有时 tee.bat 会产生正确的输出,但通常会删除其中一个数字,否则某些 stdout 和 stderr 输出会合并到一行中。它不可靠的原因与更微妙的时间问题有关。

原始程序将 stdout 和 stderr 都写入控制台很好,因为它是一个控制(写入)everyting 的进程。该过程一次只能写一件事。但是当 stderr 通过管道传输到 TEE 时,有 2 个进程试图同时写入控制台。TEST.BAT 正在编写 stdout,TEE 正在编写 stderr。没有什么可以阻止同时写入混合在一起,从而导致输出混淆。我想要获得真正的同时写入,机器必须有多个 CPU,或者至少有多个内核。但即使是单 CPU 机器也会出现问题,因为没有办法让两个进程保持同步。即使输出没有混合,也很容易出现故障。

即使 tee.exe 在我的机器上工作,我怀疑它甚至有可能失败,无论是使用不同的源输入,还是在另一台机器上。

我相信不可能可靠地得到你的结果。您可以将 stderr 重定向到 stdout,并将两者都通过管道传输到 tee。这将保持控制台输出直接,但是没有办法将标准错误输出与标准输出分开。

可靠地获得结果的唯一方法是修改源以从一开始就将每个错误消息写入两个流。但是,当您的来源超出您的控制范围时,这是不可能的。

于 2013-04-14T17:43:19.310 回答