gra*_*lem 4 windows redirect stdout batch-file stderr
请假设我有一些程序在两个主流中都有输出:err && out.作为一个例子,我们可以使用下一个bat文件:
@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
Run Code Online (Sandbox Code Playgroud)
所以在控制台的屏幕上我会看到这个输出:
1
2
3
4
5
6
7
8
9
10
Run Code Online (Sandbox Code Playgroud)
我希望看到这个输出!正如我预期的那样.但是我也希望在某个特定的'ERR.out'文件中同时看到整个错误的流.所以它的内容考虑到我的初始bat文件应该是:
2
4
6
8
10
Run Code Online (Sandbox Code Playgroud)
所以我的问题是 - 如何在BAT文件的场景中为Windows XP SP3做这个技巧?
我想使用下一个伪代码 - 但它当然不会起作用.但我希望主要逻辑是相同的:command.exe | print_it_as_is_on_screen | redirect_2_stream_to_ERR.out_file | send_whole_output_to_another_command.exe
我相信不可能可靠地做你想做的事.下面是我原来的答案,它不起作用,然后是可能有效的修改,但不能保证在所有情况下都有效,然后解释为什么它不能完美地完成.
解决问题有两个部分.
首先,您需要一个可以读取stdin并将其写入stdout和文件的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);
}
Run Code Online (Sandbox Code Playgroud)
确保tee.exe或tee.bat位于当前文件夹中,或者位于路径中的某个位置.
然后,您可以将批处理脚本的输出通过管道传输到tee,如下所示:
yourBatch | tee output.txt
Run Code Online (Sandbox Code Playgroud)
但是管道操作捕获了stdout并将其重定向到第二个程序.所以上面的行会捕获output.txt中的stdout,但是你想要捕获stderr.
其次,您需要一种机制来交换stdout和stderr.这非常简单:-)
yourBatch 3>&2 2>&1 1>&3 | tee err.txt
Run Code Online (Sandbox Code Playgroud)
我描述了上面如何在我接受的答案中工作有没有办法将stderr重定向到stdout(不合并两者)所以它可以通过管道输送到其他程序?
以上不起作用:-(
有多个时间问题阻止上述工作正常.第一个问题是在tee准备好处理输入之前有一个启动时间.批处理文件(或任何进程)可以将交错消息写入stdout和stderr,stderr通过管道连接到tee,stdout直接进入控制台.stdout会立即转发到控制台,但是当tee准备输入和输出流时,stderr会被延迟.在您的测试用例中,在开始写入控制台之前,整个stdout内容将写入控制台.所以控制台输出看起来像
1
3
5
7
9
2
4
6
8
10
Run Code Online (Sandbox Code Playgroud)
通过使用额外的批处理脚本来延迟启动程序直到发球台完成初始化,我能够在Win 7和XP虚拟机上获得正确的输出.这仅适用于gnu tee.exe.它与tee.bat不能可靠地工作.
首先,我在与测试文件夹相同的文件夹中创建delay.bat.
@echo off
ping 192.0.2.2 -n 1 -w 1000 >nul
%*
Run Code Online (Sandbox Code Playgroud)
然后,以下命令在我的机器上给出正确的结果
delay.bat test.bat 3>&2 2>&1 1>&3 | tee.exe err.txt
Run Code Online (Sandbox Code Playgroud)
如果我用tee.bat替换tee.exe,结果是不可重现的.即使使用tee.exe,它也可能无法在其他计算机上运行.
偶尔tee.bat会产生正确的输出,但通常会删除其中一个数字,否则某些stdout和stderr输出会合并为一行.它不可靠的原因与更微妙的计时问题有关.
原始程序将stdout和stderr写入控制台就好了,因为它是一个控制(写入)每个进程的进程.该过程一次只能写一件事.但是当stderr通过管道传输到TEE时,有两个进程试图同时写入控制台.TEST.BAT正在编写stdout,而TEE正在编写stderr.没有什么可以阻止同时写入混合,导致混合输出.我想要真正同时写入机器必须有多个CPU,或至少多个核心.但即使是单个CPU机器也可能存在问题,因为无法保持两个进程同步.即使输出没有混合,也很容易出现故障.
即使tee.exe在我的机器上工作,我怀疑它甚至可能会失败,无论是使用不同的源输入,还是在另一台机器上.
我相信不可能可靠地得到你的结果.您可以将stderr重定向到stdout,并将两者都管道传输到tee.这将使控制台输出保持平直,但是无法将stderr输出与标准输出分开.
可靠地获得结果的唯一方法是修改源代码,将每条错误消息写入两个流中.但是当你的资源不受控制时,这是不可能的.