ere*_*eOn 5 python windows batch-file exit-code
我面临一个奇怪的情况,我写的批处理文件报告错误的退出状态.这是一个重现问题的最小样本:
bug.cmdecho before
if "" == "" (
echo first if
exit /b 1
if "" == "" (
echo second if
)
)
echo after
Run Code Online (Sandbox Code Playgroud)
如果我运行这个脚本(使用Python但问题实际上也是以其他方式启动时),这就是我得到的:
python -c "from subprocess import Popen as po; print 'exit status: %d' % po(['bug.cmd']).wait()"
echo before
before
if "" == "" (
echo first if
exit /b 1
if "" == "" (echo second if )
)
first if
exit status: 0
Run Code Online (Sandbox Code Playgroud)
注意尽管应该如何exit status报告.0exit /b 11
现在奇怪的是,如果我删除了内部if子句(这应该无关紧要,因为exit /b 1无论如何都不应该执行)并尝试启动它:
ok.cmdecho before
if "" == "" (
echo first if
exit /b 1
)
echo after
Run Code Online (Sandbox Code Playgroud)
我再次启动它:
python -c "from subprocess import Popen as po; print 'exit status: %d' % po(['ok.cmd']).wait()"
echo before
before
(environment) F:\pf\mm_3.0.1\RendezVous\Services\Matchmaking>if "" == "" (
echo first if
exit /b 1
)
first if
exit status: 1
Run Code Online (Sandbox Code Playgroud)
现在exit status正确报告为1.
我无法理解造成这种情况的原因.嵌套if语句是非法的吗?
如何正确且可靠地发出批处理中的脚本退出状态信号?
注意:调用exit 1(不带/b)不是一个选项,因为它会终止整个解释器并阻止本地脚本的使用.
正如@dbenham指出的那样,"[i] fa命令EXIT /B在同一个命令块中被解析,然后问题就会出现,即使后续命令永远不会执行".在这种特殊情况下,IF语句的主体基本上被评估为
(echo first if) & (exit /b 1) & (if "" == "" (echo second if))
Run Code Online (Sandbox Code Playgroud)
其中&运算符是函数cmd!eComSep(即命令分隔符).的EXIT /B 1命令(功能cmd!eExit)是由全局变量设定评价cmd!LastRetCode为1,然后执行基本GOTO :EOF.当它返回时,第二个eComSep看到cmd!GotoFlag设置,因此跳过评估右侧.在这种情况下,它还忽略左侧的返回码而改为返回SUCCESS(0).这会被传递到堆栈以成为进程退出代码.
下面我已经包含了运行bug.cmd和ok.cmd的调试会话.
bug.cmd:
(test) C:\Temp>cdb -oxi ld python
Microsoft (R) Windows Debugger Version 6.12.0002.633 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: python
Symbol search path is: symsrv*symsrv.dll*
C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
(1404.10b4): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`77848700 cc int 3
0:000> g
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40)
[MSC v.1600 64 bit (AMD64)] on win32
Type "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/symbols
Executable search path is:
(1818.1a90): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`77848700 cc int 3
1:005> bp cmd!eExit
1:005> g
(test) C:\Temp>echo before
before
(test) C:\Temp>if "" == "" (
echo first if
exit /b 1
if "" == "" (echo second if )
)
first if
Breakpoint 0 hit
cmd!eExit:
00000000`4a6e8288 48895c2410 mov qword ptr [rsp+10h],rbx
ss:00000000`002fed78=0000000000000000
1:005> kc
Call Site
cmd!eExit
cmd!FindFixAndRun
cmd!Dispatch
cmd!eComSep
cmd!Dispatch
cmd!eComSep
cmd!Dispatch
cmd!Dispatch
cmd!eIf
cmd!Dispatch
cmd!BatLoop
cmd!BatProc
cmd!ECWork
cmd!ExtCom
cmd!FindFixAndRun
cmd!Dispatch
cmd!main
cmd!LUAGetUserType
kernel32!BaseThreadInitThunk
ntdll!RtlUserThreadStart
1:005> db cmd!GotoFlag l1
00000000`4a70e0c9 00 .
1:005> pt
cmd!eExit+0xe1:
00000000`4a6e8371 c3 ret
1:005> r rax
rax=0000000000000001
1:005> dd cmd!LastRetCode l1
00000000`4a70e188 00000001
1:005> db cmd!GotoFlag l1
00000000`4a70e0c9 01 .
1:005> gu;gu;gu
cmd!eComSep+0x14:
00000000`4a6e6218 803daa7e020000 cmp byte ptr [cmd!GotoFlag
(00000000`4a70e0c9)],0
ds:00000000`4a70e0c9=01
1:005> p
cmd!eComSep+0x1b:
00000000`4a6e621f 0f85bd4d0100 jne cmd!eComSep+0x1d
(00000000`4a6fafe2) [br=1]
1:005>
cmd!eComSep+0x1d:
00000000`4a6fafe2 33c0 xor eax,eax
1:005> pt
cmd!eComSep+0x31:
00000000`4a6e6235 c3 ret
1:005> r rax
rax=0000000000000000
1:005> bp ntdll!RtlExitUserProcess
1:005> g
Breakpoint 1 hit
ntdll!RtlExitUserProcess:
00000000`777c3830 48895c2408 mov qword ptr [rsp+8],rbx
ss:00000000`0029f6b0=00000000003e5638
1:005> r rcx
rcx=0000000000000000
1:005> g
ntdll!ZwTerminateProcess+0xa:
00000000`777ede7a c3 ret
1:005> g
0
Run Code Online (Sandbox Code Playgroud)
ok.cmd:
>>> po('ok.cmd').wait()
Symbol search path is: symsrv*symsrv.dll*
C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
(ce4.b94): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`77848700 cc int 3
1:002> bp cmd!eExit
1:002> g
(test) C:\Temp>echo before
before
(test) C:\Temp>if "" == "" (
echo first if
exit /b 1
)
first if
Breakpoint 0 hit
cmd!eExit:
00000000`4a6e8288 48895c2410 mov qword ptr [rsp+10h],rbx
ss:00000000`0015e808=0000000000000000
1:002> kc
Call Site
cmd!eExit
cmd!FindFixAndRun
cmd!Dispatch
cmd!eComSep
cmd!Dispatch
cmd!Dispatch
cmd!eIf
cmd!Dispatch
cmd!BatLoop
cmd!BatProc
cmd!ECWork
cmd!ExtCom
cmd!FindFixAndRun
cmd!Dispatch
cmd!main
cmd!LUAGetUserType
kernel32!BaseThreadInitThunk
ntdll!RtlUserThreadStart
1:002> gu;gu;gu
cmd!eComSep+0x2c:
00000000`4a6e6230 4883c420 add rsp,20h
1:002> p
cmd!eComSep+0x30:
00000000`4a6e6234 5b pop rbx
1:002> p
cmd!eComSep+0x31:
00000000`4a6e6235 c3 ret
1:002> r rax
rax=0000000000000001
1:002> bp ntdll!RtlExitUserProcess
1:002> g
Breakpoint 1 hit
ntdll!RtlExitUserProcess:
00000000`777c3830 48895c2408 mov qword ptr [rsp+8],rbx
ss:00000000`0015f750=00000000002b5638
1:002> r rcx
rcx=0000000000000001
1:002> g
ntdll!ZwTerminateProcess+0xa:
00000000`777ede7a c3 ret
1:002> g
1
Run Code Online (Sandbox Code Playgroud)
在ok.cmd情况下,cmd!eComSep只在堆栈跟踪中出现一次.该exit /b 1命令被评估为右侧操作数,因此查看的代码GotoFlag永远不会运行.而是返回代码1传递到堆栈以成为进程退出代码.
| 归档时间: |
|
| 查看次数: |
791 次 |
| 最近记录: |