Why doesn't my stderr redirection end after command finishes? And how do I fix it?

In Windows, either in command line or a batch file, the command DIR 2>NUL: 3>&2 (you can replace DIR with anything, even if isn't a file or a command) will make all errors from then on missing unless you write 2>CON: after every command. Why is CMD even doing this? And how do you get it back to normal without starting a new CMD process? DIR 2>CON: 3>&2 will only work for that command alone.

EDIT: This will work with files as well. DIR 2>TEXT.TXT 3>&2 Any errors after that will be appended to the file.


Here is a test script that reproduces the problem you are seeing.

@echo off
2>nul 3>nul (
  echo I want to see stream1
  1>&2 echo I don't want to see this stream2
  1>&3 echo I don't want to see this stream3
)
echo stream1 works fine
1>&2 echo stream2 is now "permanently" void. I don't see this.
1>&3 echo stream3 works fine

And here is the output

I want to see stream1
stream1 works fine
stream3 works fine

stderr (stream 2) has been disabled "permanently", even for the parent CMD.EXE shell.

You can avoid the "permanent" aspect by doing your redirection in stages:

@echo off
2>nul (
  3>nul (
    echo I want to see stream1
    1>&2 echo I don't want to see this stream2
    1>&3 echo I don't want to see this stream3
  )
)
echo stream1 works fine
1>&2 echo stream2 works fine
1>&3 echo stream3 works fine

And here is the desired output:

I want to see stream1
stream1 works fine
stream2 works fine
stream3 works fine

I don't really understand what is going on. But I have done some interesting experiments. Check out this thread: http://www.dostips.com/forum/viewtopic.php?f=3&t=2836&start=30

Addendum

As Erbert has discovered and shared in his comment, the fix is even easier if you just switch the order of the redirection - no need to stage it.

@echo off
3>nul 2>nul (
  echo I want to see stream1
  1>&2 echo I don't want to see this stream2
  1>&3 echo I don't want to see this stream3
)
echo stream1 works fine
1>&2 echo stream2 works fine
1>&3 echo stream3 works fine

Update 2012-04-03 I believe I finally understand the mechanics of Windows CMD.EXE redirection. I have a working theory that fully accounts for all the weird behavior, including why reversing the order prevents the "permanent" redirection. It also explains Aacini's observation that handle 3 appears to be connected to CON: (It isn't, it is actually undefined as per Windows documentation).

The key points are:

1 - Whenever a handle (stream) is redirected, the original definition is transferred to the first available undefined handle. Successive redirections are always carried out from left to right.

2 - When the redirection is over, the original definitions are normally restored. But if there is a chain of redirections, then the restoration is only done 1 level deep. This is the source of the "permanent" redirection.

Edit 2014-12-19: Put another way, the restoration appears to be done using a queue structure (FIFO - First In First Out), when it should have been implemented as a stack (LIFO - Last In First Out).

3 - When CMD.EXE carries out the redirection, it saves the current definition in the undefined handle first, then it redirects the first handle. If the first handle is redirected to the originally undefined handle, then it is effectively redirected to its original definition! That is why echo hello 1>&3 outputs to the console.

The full theory and test cases are available in two consecutive posts at http://www.dostips.com/forum/viewtopic.php?p=14612#p14612.


I apologize for post this as an answer instead of a comment, but my "comment" is too large...

In the MS-DOS standard, all running programs have open these Standard Handles: 0-STDIN (keyboard), 1-STDOUT (screen), 2-STDERR (screen), 3-STDAUX (serial port) and 4-STDPRN (printer). Although Windows documentation clearly indicate that handles 3-9 are undefined, handle 3 have a special treatment by CMD.EXE. I have three reasons to think this:

1- Handle 3 is connected to CON: device (keyboard for input, screen for output); handles 4-9 does not:

C>ver

Microsoft Windows XP [Version 5.1.2600]

C>echo To handle 3 >&3
To handle 3

C>echo To handle 4 >&4
The handle could not be duplicated
during redirection of handle 1.

C>set /P var=From handle 3: <&3
From handle 3: Value entered in keyboard

C>echo %var%
Value entered in keyboard

C>set /P var=From handle 4: <&4
The handle could not be duplicated
during redirection of handle 0.

2- The strange behavior of handle 3 stated in the present topic, that was solved in two differents ways. I discovered that if handles 0, 1 or 2 are redirected with handle 3 (and possibly handles 4-9), the redirection of handles 0, 1 or 2 becomes "permanent"; this behaviour doesn't happens if the handle 0, 1 or 2 are the last handle(s) in the list of redirections that include handle 3. This problem is entirely avoided if handles 0, 1 or 2 are redirected with handles 4-9 in any order, but not handle 3.

3- The results obtained with my TypeOfHandle.com program. This program is a pure MS-DOS executable file that check the handle given in its parameter and return via errorlevel a value of 3 if the handle is connected to the CONsole device, or a value of 128 if the handle is redirected to a disk file. These are the results:

C>typeofhandle 0

C>echo %errorlevel%
3

C>typeofhandle 0 < anyFile.txt

C>echo %errorlevel%
128

C>typeofhandle 1

C>echo %errorlevel%
3

C>typeofhandle 1 > anyFile.txt

C>echo %errorlevel%
128

C>typeofhandle 3

C>echo %errorlevel%
0

C>typeofhandle 3 <&3 anyFile.txt

C>echo %errorlevel%
0

C>typeofhandle 3 >&3 anyFile.txt

C>echo %errorlevel%
0

Handles 3-9 behaves strange in other aspect:

The results obtained with my SetFilePointer.com program. Although handles 3-9 may be used in a Batch file to achieve input/output from/to several files at once, such mechanism only allows sequential access because my SetFilePointer program can't move the File Pointer of handles 3-9. SetFilePointer program correctly works on handles 0, 1 and 2; this feature allows to write a complete Relational Database application in Batch. This topic is described in detail at this post