最后,我使用move nul 2>&0
在我的脚本中“突然”关闭当前的cmd窗口,从而立即中止/关闭正在运行的bat。
我不能说它在剧本中就像自杀一样,但我通常这么称呼它。
事实上,这会导致自动终止,并且在当前 cmd 窗口中也会扩展/达到效果。
这不仅仅是关闭/结束正在运行的球棒的事情。
主要用于一些批次,其中变量的值"%cmdcmdline%"
通过执行点击调用来响应,而不是简单地关闭球棒,还会关闭通过点击球棒激活/加载的窗口。
我想知道的是:
- 如何解释这种行为?
- 这是一个错误吗?
- 将来它可能不会表现得一样吗?
一些代码示例:
@echo off
title <nul
title to kill or not to kill?
mode 50,05
echo\%cmdcmdline%|find "%~0" >nul && (
echo\
echo\ so sorry bro, I'll kill myself
echo\ and this CMD.exe window too!
timeout 5 /nobreak
move nul 2>&0
)
echo/ will run this code bro!
echo/
echo/ keep itself alive: %date% %time:~0,5%
echo/
pause
答案1
我很难理解你所说的“当前 cmd 窗口”这句话 - 不确定你的意思。在我看来,你把两个不同的概念结合在一起了。
- 有一个控制台进程——与终端类似,但不完全相同
- 控制台窗口中正在执行 cmd.exe 进程
您的命令导致 cmd.exe 进程崩溃,并且您的控制台进程干净关闭,因为其中不再有进程在运行。
如果您在额外的子 cmd.exe 进程中运行命令,这一点很容易证明 - 子 cmd.exe 崩溃,但父进程仍然有效,因此控制台窗口也保持运行。以下是从控制台中运行的全新 cmd.exe 进程开始的示例。
Microsoft Windows [Version 10.0.18363.1256]
(c) 2019 Microsoft Corporation. All rights reserved.
P:\>set context=outer
P:\>cmd
Microsoft Windows [Version 10.0.18363.1256]
(c) 2019 Microsoft Corporation. All rights reserved.
P:\>set context=inner
P:\>move nul 2>&0
P:\>set context
context=outer
P:\>
最后的 context = outer 证明内部环境已丢失,原因是内部进程已崩溃。如果我随后发出 EXIT 命令,则外部 cmd.exe 进程将终止并且控制台将关闭。
MOVE
现在来看看为什么你的命令会导致 cmd.exe 崩溃。你对命令或设备的使用没有什么特别之处nul
。关键是 cmd.exe 在重定向到 stdin 后试图将错误消息写入 stderr。显然这行不通,所以写入失败 - 这是导致 cmd.exe 崩溃的触发器。
通过除以零误差可以达到同样的效果SET /A 1/0 2>&0
。
值得注意的是,重定向实际上成功了。重定向并不关心 stdin 是否能接受输出,它只是盲目地执行重定向。只有当 cmd.exe 错误写入例程尝试实际写入重定向的 stderr 时,才会发生失败。
如果您只是重定向一个不写入 stderr 的命令,则很容易证明重定向成功。例如,echo Hello world 2>&0
执行无意义的重定向并成功将消息写入 stdout。
在 DosTips 上的某个地方,我有一些帖子,其中我调查了 cmd.exe 如何处理 I/O 错误。我希望我能找到那篇帖子。我相信我的测试涉及将 stdout 或 stderr 重定向到可移动设备,然后暂停。在暂停期间,我会移除该设备,然后继续。然后,当我尝试通过 stdout 或 stderr 写入丢失的设备时,我会观察行为。据我记得,所有对 stderr 的失败写入都会立即导致 cmd.exe 崩溃。
这是错误还是设计缺陷?不确定。有人可能会说,如果错误报告失败,最安全的做法可能是终止。但我不相信这是计划中的“功能”,因为当写入 stdout 失败时,不会返回任何错误消息或错误!输出只是消失在以太中,cmd.exe 会继续运行,好像什么坏事都没有发生。批处理在写入 stdout 时不可能检测到故障。我觉得这太糟糕了。我只能假设 cmd.exe 的开发人员从未考虑过这种可能性。因此,如果这是真的,那么 stderr 故障崩溃的事实很容易成为(幸福的?)意外。
stdout 和 stderr 行为之间还存在一些其他差异。两者都是缓冲的,但如果发生缓冲区溢出,它们的行为会有所不同。我希望我能记得那个差异是什么 :-(
更新
我发现我的一些调查https://www.dostips.com/forum/viewtopic.php?t=6881
写入 stdout 比我上面的描述要复杂一些。Cmd.exe 有多个尝试写入 stdout 的内部例程。例如:
- ECHO 命令
- SET /P 消息(提示输入)
- 批处理命令行的命令行提示和回显(当 ECHO 为 ON 时)
- 输出诸如 DIR 等命令。
当写入失败时,其中一些写入 stdout 的入口点可能会有自己的行为。例如,上面的链接描述了 ECHO 和 SET /P 之间的区别。
我不确定,但我相信 stderr 的错误消息是不同的。我相信所有错误报告都通过一个内部例程进行,当发生写入错误时,该例程会崩溃。上面的链接并没有真正调查这一点。我仍在寻找更深入调查 I/O 错误(和缓冲问题)的后续帖子