盗梦终端之 Windows cmd 实现
盗梦终端是什么?请参考 TualatriX 这篇很火的文章。
如果想在 Windows 的 Command Prompt(cmd)下实现相同的把戏,首先遇到的问题是 cmd 没有内置的 SHLVL 环境变量来获取递归层次。不过这个很好解决,我们只要利用 cmd 的 AutoRun 机制,让它在每次启动时计数即可。由于子进程会继承父进程的环境变量,这个计数值就会随着递归层次的增长而增长。具体方法是添加如下注册表项:
- Windows Registry Editor Version 5.00
- [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
- "AutoRun"="set /a SHLVL=SHLVL+1 > NUL"
此时在 cmd 中执行 echo %SHLVL%,即可查看当前位于第几层递归调用。如果想测试一下 cmd 递归调用最多能有多深,那么同样可以利用 AutoRun 机制不断启动 cmd 实例。这里我们保留 set 命令的输出结果,以便看到每次迭代的 SHLVL 值。
- Windows Registry Editor Version 5.00
- [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
- "AutoRun"="set /a SHLVL=SHLVL+1 & cmd"
由于无从得到 cmd 的源代码,我不知道 cmd 递归执行有没有深度限制。在一台 Windows 7 机器和一台 Windows Server 2008 机器上测试,当递归达到 3000+ 和 6000+ 层时,两个系统分别报出了“The system cannot execute the specified program”错误,从 Taskman 看应该是内存耗尽,此时其他程序也难以正常启动。
不过这个 SHLVL 可能没有 bash 中的 SHLVL 那样有用:在 bash 中,直接运行脚本会新启动一个 bash 进程,因此在嵌套调用的脚本中可以使用 SHLVL 得知嵌套层次;但在 cmd 中,直接运行批处理文件(或使用 call 命令嵌套调用批处理文件)是在当前 cmd 进程中执行命令,除非用户通过 cmd /c 强制开启新进程运行嵌套的批处理文件,否则当前 SHLVL 永远和外部 cmd 的 SHLVL 一致。
那么使用 call 方式嵌套调用批处理文件时,有没有办法能够做到不用修改每一层的批处理文件而得知嵌套层次呢?我暂时没有发现。call 方式允许的递归层次受限于 cmd 进程的栈大小。在我测试的机器上,一个递归调用自身的批处理文件在递归 1240 层之后占用了 90% 的栈空间,此时 cmd 将批处理终止。
- ****** B A T C H R E C U R S I O N exceeds STACK limits ******
- Recursion Count=1240, Stack Usage=90 percent
- ****** B A T C H PROCESSING IS A B O R T E D ******
另外想到了几年前研究过的一个与之相关的问题:在同一个 cmd 进程中,setlocal 命令设置的本地环境变量栈是有深度限制的,目前版本限制为 32 层。cn-dos 上有一个帖子也讨论过这个问题。
