Windows 为什么';我的stderr重定向不会在命令完成后结束吗?我该如何修复它?

Windows 为什么';我的stderr重定向不会在命令完成后结束吗?我该如何修复它?,windows,command-line,batch-file,cmd,Windows,Command Line,Batch File,Cmd,在Windows中,无论是在命令行还是批处理文件中,命令DIR 2>NUL:3>&2(您可以用任何东西替换DIR,即使它不是文件或命令)都将从那时起丢失所有错误,除非您在每个命令之后写入2>CON:。CMD为什么要这么做?如何在不启动新CMD流程的情况下使其恢复正常dir2>CON:3>&2仅适用于该命令 编辑:这也适用于文件dir2>TEXT.TXT 3>&2之后的任何错误都将附加到文件。问题在于3>&2。文件描述符3无效,它似乎以某种方式扰乱了Windows。别管它,你不需要它 有关完整的处

在Windows中,无论是在命令行还是批处理文件中,命令
DIR 2>NUL:3>&2
(您可以用任何东西替换
DIR
,即使它不是文件或命令)都将从那时起丢失所有错误,除非您在每个命令之后写入
2>CON:
。CMD为什么要这么做?如何在不启动新CMD流程的情况下使其恢复正常
dir2>CON:3>&2
仅适用于该命令


编辑:这也适用于文件
dir2>TEXT.TXT 3>&2
之后的任何错误都将附加到文件。

问题在于
3>&2
。文件描述符3无效,它似乎以某种方式扰乱了Windows。别管它,你不需要它


有关完整的处理方法,请参阅。

这是一个测试脚本,它再现了您看到的问题

@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
这是输出

I want to see stream1
stream1 works fine
stream3 works fine
stderr(流2)已被“永久”禁用,即使对于父CMD.EXE shell也是如此

您可以通过分阶段执行重定向来避免“永久性”方面:

@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
以下是所需的输出:

I want to see stream1
stream1 works fine
stream2 works fine
stream3 works fine
我真的不明白发生了什么事。但我做了一些有趣的实验。查看此线程:

附录

正如Erbert在他的评论中发现并分享的那样,如果您只需切换重定向的顺序,修复就更容易了——无需进行阶段性修复

@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
更新2012-04-03 我相信我终于了解了Windows CMD.EXE重定向的机制。我有一个有效的理论,充分解释了所有奇怪的行为,包括为什么颠倒顺序会阻止“永久”重定向。它还解释了Aacini的观察结果,即句柄3似乎与CON有关:(事实并非如此,根据Windows文档,它实际上是未定义的)

重点是:

1-无论何时重定向句柄(流),原始定义都会传输到第一个可用的未定义句柄。连续重定向始终从左向右执行

2-重定向结束后,通常恢复原始定义。但是如果存在一系列重定向,那么恢复只需进行1级深度。这是“永久”重定向的来源

编辑2014-12-19:换句话说,恢复似乎是使用队列结构(FIFO-先进先出)完成的,而它本应作为堆栈(后进先出-后进先出)实现

3-当CMD.EXE执行重定向时,它首先将当前定义保存在未定义的句柄中,然后重定向第一个句柄。如果第一个句柄被重定向到最初未定义的句柄,那么它实际上被重定向到其原始定义!这就是为什么
echo hello 1>&3
输出到控制台的原因


完整的理论和测试案例可以在两个连续的帖子中找到。

我很抱歉将此帖子作为答案而不是评论,但是我的“评论”太大了

在MS-DOS标准中,所有运行的程序都打开了这些标准句柄:0-STDIN(键盘)、1-STDOUT(屏幕)、2-STDERR(屏幕)、3-STDAUX(串行端口)和4-STDPN(打印机)。尽管Windows文档明确指出句柄3-9未定义,但句柄3通过CMD.EXE进行了特殊处理。我有三个理由这样想:

1-手柄3连接到CON:设备(键盘用于输入,屏幕用于输出);句柄4-9不:

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.
C>ver
Microsoft Windows XP[版本5.1.2600]
C> 回显处理3>&3
处理3
C> 回显处理4>&4
无法复制句柄
在句柄1的重定向期间。
C> set/P var=从句柄3:回显%var%
在键盘上输入的值

C> set/P var=From handle 4:Stdout是描述符1,stderr是描述符2。不要重定向3。如果我正在使用一个输出到3的批处理文件,并且我需要重定向该文件,该怎么办?我不太确定3到底是什么,但这可能是问题的根源。Windows应该也支持其他句柄。“DIR>NUL 3>&1”更糟糕,提示都没有了。我看不到标准输出。它说支持3-9。我希望在批处理中有3个不同的输出:@ECHO OFF ECHO这是1 ECHO这是2 1>&2 ECHO这是3 1>&3,然后当我运行它并想隐藏2和3(“FILE.BAT 2>NUL:3>&2”)时,它将被永久设置。@Erbert,它说3-9是未定义的,取决于工具。除非该工具明确记录了它对特定文件句柄重定向的反应,否则不要这样做。我不太明白你想要得到什么样的结果-你说的“3种不同的输出”是什么意思?但正如阿提拉指出的,CMD的工作是处理这些句柄。我的批处理文件使用3个输出句柄,但我刚刚编写了一个批处理文件如何写入句柄3的示例。我相信有些程序也能做到这一点。说这些描述符是无效的是误导。您链接的文档说明它们是
未定义的
。字符串“
valid
”未出现在该文档页面上。因此,只有当“无效”包含“未定义”的假设成立时,它们才是无效的。谢谢,这成功了,+1来自我!但是我自己也做了一些实验,使用相反的顺序(2之前的3)似乎有效,并且比您的示例“FILE.BAT 3>NUL:2>&3”更简单。@Erbert-切换顺序的好主意
FILE.BAT 3>NUL 2>NUL
也可以工作。+5(如果我可以的话),这是对流机制的一个非常令人印象深刻的分析,它不是很明显是如何工作的works@Andry-不,你没有完全理解我的答案。如果你这样做了,你就会理解你的行为。首先,1中的现有值(控制台)保存在3中,然后1被定向到1.log。然后将3中的现有值(控制台)保存在4中,然后将3重定向到2.log。执行命令并将123写入1.log。命令终止,1 i
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