如何正确地批量报告退出状态? [英] How to properly report an exit status in batch?

查看:19
本文介绍了如何正确地批量报告退出状态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个奇怪的情况,我编写的批处理文件报告了错误的退出状态.这是重现问题的最小示例:

I'm facing a weird situation where a batch file I wrote reports an incorrect exit status. Here is a minimal sample that reproduces the problem:

echo before

if "" == "" (
        echo first if
        exit /b 1

        if "" == "" (
                echo second if
        )
)

echo after

如果我运行此脚本(使用 Python,但实际上在以其他方式启动时也会出现此问题),我会得到以下结果:

If I run this script (using Python but the problem actually occurs when launched in other ways too), here is what I get:

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

注意 exit status 如何报告为 0 即使 exit/b 1 应该使它成为 1.

Note how exit status is reported as 0 even though exit /b 1 should make it be 1.

现在奇怪的是,如果我删除内部的 if 子句(这应该无关紧要,因为 exit/b 1 之后的所有内容都不应该被执行)并尝试启动它:

Now the weird thing is that if I remove the inside if clause (which should not matter because everything after exit /b 1 should not be executed anyway) and try to launch it:

echo before

if "" == "" (
        echo first if
        exit /b 1
)

echo after

我再次启动它:

python -c "from subprocess import Popen as po; print 'exit status: %d' % po(['ok.cmd']).wait()"

echo before
before

(environment) F:pfmm_3.0.1RendezVousServicesMatchmaking>if "" == "" (
echo first if
 exit /b 1
)
first if
exit status: 1

现在 exit status 正确报告为 1.

Now the exit status is correctly reported as 1.

我无法理解是什么导致了这种情况.嵌套 if 语句是否违法?

I'm at loss understanding what is causing this. Is it illegal to nest if statements ?

如何正确可靠地批量发出脚本退出状态的信号?

注意:调用exit 1(没有/b)不是一种选择,因为它会杀死整个解释器并阻止本地脚本的使用.

Note: calling exit 1 (without the /b) is not an option as it kills the whole interpreter and prevents local script usage.

推荐答案

正如@dbenham 所指出的,[i]fa 命令在 EXIT/B 之后被解析,在同一个命令块中,然后即使随后的命令从未执行过,问题也会出现".在这种特殊情况下,IF 语句的主体基本上被评估为

As @dbenham notes, "[i]f a command is parsed after EXIT /B, within the same command block, then the problem manifests, even though the subsequent command never executes". In this particular case the body of the IF statement is basically evaluated as

(echo first if) & (exit /b 1) & (if "" == "" (echo second if))

其中 & 运算符是函数 cmd!eComSep(即命令分隔符).EXIT/B 1 命令(函数cmd!eExit)通过将全局变量cmd!LastRetCode 设置为1,然后基本上执行<代码>转到:EOF.当它返回时,第二个 eComSep 看到 cmd!GotoFlag 已设置,因此跳过评估右侧.在这种情况下,它也会忽略左侧的返回码,而是返回 SUCCESS (0).这被向上传递堆栈成为进程退出代码.

where the & operator is the function cmd!eComSep (i.e. command separator). The EXIT /B 1 command (function cmd!eExit) is evaluated by setting the global variable cmd!LastRetCode to 1 and then basically executing GOTO :EOF. When it returns, the second eComSep sees cmd!GotoFlag is set and so skips evaluating the right-hand side. In this case, it also ignores the return code of the left-hand side to instead return SUCCESS (0). This gets passed up the stack to become process exit code.

下面我包含了运行 bug.cmd 和 ok.cmd 的调试会话.

Below I've included the debug sessions for running bug.cmd and 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

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

在 ok.cmd 的情况下,cmd!eComSep 在堆栈跟踪中只出现一次.exit/b 1 命令被评估为右侧操作数,因此查看 GotoFlag 的代码永远不会运行.取而代之的是,返回码 1 向上传递堆栈,成为进程退出代码.

In the ok.cmd case, cmd!eComSep only appears once in the stack trace. The exit /b 1 command is evaluated as the right-hand side operand, so the code that looks at GotoFlag never runs. Instead the return code of 1 gets passed up the stack to become the process exit code.

这篇关于如何正确地批量报告退出状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆