35

I'm working in a Windows CMD.EXE environment and would like to change the output of stdout to match that of stderr so that I can pipe error messages to other programs without the intermediary of a file.

I'm aware of the 2>&1 notation, but that combines stdout and stderr into a single stream.

What I'm thinking of would be something like this:

program.exe 2>&1 | find " "

But that combines stdout and stderr just like:

program.exe | find " " 2>&1

I realize that I could do...

program 2>file
type file | find " "
del file

But this does not have the flexibility and power of a program | find " " sort of notation. Doing this requires that program has finished with its output before that output can be processed.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
James K
  • 4,005
  • 4
  • 20
  • 28

2 Answers2

52

Interesting question :-)

CMD processes redirection from left to right. You want to first redirect 2 (stderr) to &1 (stdout), then redirect 1 (stdout) to something else. At this point stderr will still be redirected to the previous definition of stdout. The pipe will still work with the old definition of stdout (which now contains stderr).

If you don't care about stdout then you can redirect to nul

program.exe 2>&1 1>nul | find " "


If you want to capture stdout to a file then redirect to a file

program.exe 2>&1 1>yourFile | find " "


If you still want to see stdout on the console, but you only want to pipe stderr to FIND, then you can redirect 1 to con:

program.exe 2>&1 1>con: | find " "

Note that there is a subtle difference between the original definition of stdout and con:. For example, cls >con: does not clear the screen, it prints a funny character to the screen instead.

It is possible to truly swap stdout and stderr if you use a 3rd (initially unused) file handle. 1 and 3 will contain original definition of stderr, and 2 will contain original definition of stdout.

program.exe 3>&2 2>&1 1>&3 | find " "

Actually there is an additional file handle defined every time a redirection is performed. The original definition is saved in the first available unused file handle. Assume there has not been any redirection prior to issuing the above command. 3>&2 does not save the original definition of 3 because 3 was not previously defined. But 2>&1 saves the original definition of stderr in 4 (3 has already been used), and 1>&2 saves the original definition of stdout in 5.

So technically, the explicit redirection of 3 is not needed to swap stderr and stdout

program.exe 2>&1 1>&3 | find " "

2>&1 saves stderr in 3 and 2 is redirected to &1 (stdout). 1>&3 saves stdout in 4 and 1 is redirected to &3 (stderr).

But the above will only work properly if you are positive that 3 has not already been defined prior to issuing the command. It is much safer to explicitly define 3 as in my prior code example.

See Why doesn't my stderr redirection end after command finishes? And how do I fix it? for some really wild adventures with redirection :-)

Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • @JamesK - Added another option that may be more robust than using con: – dbenham Sep 05 '12 at 12:04
  • But for me both `3>&2 2>&1 1>&3` and `2>&1 1>&3` combine `stdout` and `stderr`. – James K Sep 05 '12 at 17:09
  • @JamesK - Really? It works for me on both Vista and Windows 7. I haven't had a chance to check on XP. `DIR DoesNotExist 2>&1 1>&3 | FIND ""` passes the empty directory listing to the screen but strips the error message, whereas `DIR DoesNotExist 2>&1 1>&3 | FIND /V ""` passes both the empty directory listing and the error message. – dbenham Sep 05 '12 at 18:07
  • `DIR DoesNotExist 2>&1 1>&3 | FIND ""` does strip the error message, while `... | find " "` shows both. I was assuming that `DIR DoesNotExist 2>&1 1>&3` would be the same as `DIR DoesNotExist 2>&1 1>&3 | find " "`, because `DIR DoesNotExist 2>&1 1>nul` works. Also, I'm not sure how `stderr`==`""`, so I don't understand how the `... | find ""` works. (I am using Windows 7 Ultimate x64.) – James K Sep 05 '12 at 19:49
  • @JamesK - I think your problem may be with your testing. `... | FIND " "` will match any line containing a space. `... | FIND /V ""` is a FIND quirk that matches all lines - I've never understood why, but it is useful. `... | FIND ""` will not match any line. I chose those forms so I could verify that stderr was getting piped and stdout was not. – dbenham Sep 05 '12 at 22:45
  • Bizarre. I'm now much more informed on how redirection works, thanks again! – James K Sep 06 '12 at 00:24
3

From looking at http://support.microsoft.com/kb/110930 it seems that you want 2>&1 1>NUL. So something like the following should work for you:

test.exe 2>&1 1>NUL | find "someErrorString"
nithins
  • 3,172
  • 19
  • 21