Reassign sterr to file, but leave all output on the screen intact (as_is). [Windows XP - BAT files]

Pls, imagine that I have a program that displays in both main threads: err && outside. As an example, we can use the following 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 conclusion! As I expected. But also I want to see the entire stream of errors at the same time in a specific file "ERR.out". Thus, the content given my initial bat file should be:

 2 4 6 8 10 

So my question is: how to do this trick for Windows XP SP3 in a BAT file script?

I would like to use the following pseudo code, but it will not work, of course. But I hope 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
source share
1 answer

I believe that it is impossible to reliably do what you want. Below is my initial answer, which does not work, followed by a modification that can work, but is not guaranteed to work in all situations, followed by an explanation of why this cannot be done perfectly.

There are two parts to solving your problem.

First , you need a tee program that can read stdin and write it to both stdout and the file. You can use tee.exe from gnuwin32 CoreUtils for Windows , or you can use the following hybrid JScript / batch file - 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); } 

Make sure tee.exe or tee.bat is either in your current folder or somewhere inside your path.

Then you can execute the output from your batch script in tee as follows:

 yourBatch | tee output.txt 

But the pipe operation captures stdout and redirects it to the second program. So the line above will capture stdout in output.txt, but instead you want to capture stderr.

Second , you need a mechanism for exchanging stdout and stderr. This is surprisingly easy :-)

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

I describe how this works in my accepted answer to Is there a way to redirect ONLY stderr to stdout (not combine the two) so that it can be passed to other programs?

The above does not work: - (

There are several synchronization issues that prevent the above from working properly. The first problem is that there is a start time before tee is ready to process the input. A batch file (or any other process) can write interlaced messages to stdout and stderr, with stderr passed to tee and stdout directly to the console. Stdout is immediately sent to the console, but stderr is delayed, and tee prepares input and output streams. In the test case, all stdout content is written to the console before tee even starts writing to the console. So the console output looks like

 1 3 5 7 9 2 4 6 8 10 

I can get the correct output on both Win 7 and the XP virtual machine, using the optional batch version of the script to delay the launch of the program until the tee completes initialization. This only works with gnu tee.exe. This does not work reliably with tee.bat.

First I create a delay.bat file in the same folder as the test folder.

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

Then the following command gives the correct results on my machine

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

The results are not reproduced if I replace tee.bat with tee.exe. It may not work on other machines even with tee.exe.

Sometimes tee.bat produces the correct output, but usually one of the numbers is discarded, otherwise the output of stdout and stderr is combined into one line. The reason it is unreliable is related to the more subtle problem of time.

The original program writes both stdout and stderr to the console just fine, because it is one process that controls (writes) each. A process can record only one at a time. But when stderr is sent to TEE, then there are two processes that try to write to the console at the same time. TEST.BAT writes stdout, and TEE writes stderr. There is nothing that could stop simultaneous recording due to mixing, which led to mixing output. I believe that in order to get a truly simultaneous recording, a machine must have several processors or at least several cores. But even a single processor processor may have problems, because it is impossible to synchronize both processes. Even if the output does not mix, it can easily fail.

Even though tee.exe is running on my machine, I suspect it is even possible to crash, either using a different source or on another machine.

I believe that it is not possible to reliably get your results. You can redirect stderr to stdout and swipe to tee. This will result in direct console output, but then there is no way to separate stderr output from stdout.

The only way to reliably get your result is to change the source to record each error message in two streams right from the start. But this is not possible if your source is out of your control.

+5
source

All Articles