正如@dbenham指出的那样,“ [i] fa命令
EXIT/B在同一命令块中的之后解析,即使随后的命令从不执行,问题也会显现出来”。在这种特殊情况下,
IF语句的主体基本上被评估为
(echo first if) & (exit /b 1) & (if "" == "" (echo second if))
其中
&运算符是函数
cmd!eComSep(即命令分隔符)。的
EXIT /B1命令(功能
cmd!eExit)是由全局变量设定评价
cmd!LastRetCode为1,然后执行基本
GOTO:EOF。当它返回时,第二个
eComSepsees
cmd!GotoFlag被设置,因此跳过评估右侧。在这种情况下,它也忽略左侧的返回码,而是返回
SUCCESS(0)。这被传递到堆栈上,成为进程退出代码。
下面,我包括了用于运行bug.cmd和ok.cmd的调试会话。
bug.cmd:
(test) C:Temp>cdb -oxi ld pythonMicrosoft (R) Windows Debugger Version 6.12.0002.633 AMD64Copyright (c) Microsoft Corporation. All rights reserved.CommandLine: pythonSymbol search path is: symsrv*symsrv.dll* C:Symbols*http://msdl.microsoft.com/download/symbolsExecutable search path is:(1404.10b4): Break instruction exception - pre 80000003 (first chance)ntdll!LdrpDoDebuggerBreak+0x30:00000000`77848700 cc int 30:000> gPython 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40)[MSC v.1600 64 bit (AMD64)] on win32Type "help", "copyright", "credits" or "license" for more information.>>> from subprocess import Popen as po>>> po('bug.cmd').wait()Symbol search path is: symsrv*symsrv.dll* C:Symbols*http://msdl.microsoft.com/download/symbolsExecutable search path is:(1818.1a90): Break instruction exception - pre 80000003 (first chance)ntdll!LdrpDoDebuggerBreak+0x30:00000000`77848700 cc int 31:005> bp cmd!eExit1:005> g(test) C:Temp>echo beforebefore(test) C:Temp>if "" == "" (echo first if exit /b 1 if "" == "" (echo second if ))first ifBreakpoint 0 hitcmd!eExit:00000000`4a6e8288 48895c2410 mov qword ptr [rsp+10h],rbx ss:00000000`002fed78=00000000000000001:005> kcCall Sitecmd!eExitcmd!FindFixAndRuncmd!Dispatchcmd!eComSepcmd!Dispatchcmd!eComSepcmd!Dispatchcmd!Dispatchcmd!eIfcmd!Dispatchcmd!BatLoopcmd!BatProccmd!ECWorkcmd!ExtComcmd!FindFixAndRuncmd!Dispatchcmd!maincmd!LUAGetUserTypekernel32!baseThreadInitThunkntdll!RtlUserThreadStart1:005> db cmd!GotoFlag l100000000`4a70e0c9 00 .1:005> ptcmd!eExit+0xe1:00000000`4a6e8371 c3 ret1:005> r raxrax=00000000000000011:005> dd cmd!LastRetCode l100000000`4a70e188 000000011:005> db cmd!GotoFlag l100000000`4a70e0c9 01 .1:005> gu;gu;gucmd!eComSep+0x14:00000000`4a6e6218 803daa7e020000 cmp byte ptr [cmd!GotoFlag (00000000`4a70e0c9)],0 ds:00000000`4a70e0c9=011:005> pcmd!eComSep+0x1b:00000000`4a6e621f 0f85bd4d0100 jne cmd!eComSep+0x1d (00000000`4a6fafe2) [br=1]1:005>cmd!eComSep+0x1d:00000000`4a6fafe2 33c0 xor eax,eax1:005> ptcmd!eComSep+0x31:00000000`4a6e6235 c3 ret1:005> r raxrax=00000000000000001:005> bp ntdll!RtlExitUserProcess1:005> gBreakpoint 1 hitntdll!RtlExitUserProcess:00000000`777c3830 48895c2408 mov qword ptr [rsp+8],rbx ss:00000000`0029f6b0=00000000003e56381:005> r rcxrcx=00000000000000001:005> gntdll!ZwTerminateProcess+0xa:00000000`777ede7a c3 ret1:005> g0ok.cmd:
>>> po('ok.cmd').wait()Symbol search path is: symsrv*symsrv.dll* C:Symbols*http://msdl.microsoft.com/download/symbolsExecutable search path is:(ce4.b94): Break instruction exception - pre 80000003 (first chance)ntdll!LdrpDoDebuggerBreak+0x30:00000000`77848700 cc int 31:002> bp cmd!eExit1:002> g(test) C:Temp>echo beforebefore(test) C:Temp>if "" == "" (echo first if exit /b 1)first ifBreakpoint 0 hitcmd!eExit:00000000`4a6e8288 48895c2410 mov qword ptr [rsp+10h],rbx ss:00000000`0015e808=00000000000000001:002> kcCall Sitecmd!eExitcmd!FindFixAndRuncmd!Dispatchcmd!eComSepcmd!Dispatchcmd!Dispatchcmd!eIfcmd!Dispatchcmd!BatLoopcmd!BatProccmd!ECWorkcmd!ExtComcmd!FindFixAndRuncmd!Dispatchcmd!maincmd!LUAGetUserTypekernel32!baseThreadInitThunkntdll!RtlUserThreadStart1:002> gu;gu;gucmd!eComSep+0x2c:00000000`4a6e6230 4883c420 add rsp,20h1:002> pcmd!eComSep+0x30:00000000`4a6e6234 5b pop rbx1:002> pcmd!eComSep+0x31:00000000`4a6e6235 c3 ret1:002> r raxrax=00000000000000011:002> bp ntdll!RtlExitUserProcess1:002> gBreakpoint 1 hitntdll!RtlExitUserProcess:00000000`777c3830 48895c2408 mov qword ptr [rsp+8],rbx ss:00000000`0015f750=00000000002b56381:002> r rcxrcx=00000000000000011:002> gntdll!ZwTerminateProcess+0xa:00000000`777ede7a c3 ret1:002> g1在ok.cmd情况下,
cmd!eComSep在堆栈跟踪中仅出现一次。该
exit /b1命令被评估为右侧操作数,因此查看的代码
GotoFlag永远不会运行。取而代之的是,返回代码1沿栈传递,成为流程退出代码。



