当前位置: 首页 > 面试题库 >

如何正确批量报告退出状态?

唐康安
2023-03-14
问题内容

我面临一种奇怪的情况,即我编写的​​批处理文件报告了错误的退出状态。这是重现该问题的最小示例:

bug.cmd

echo before

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

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

echo after

如果我运行此脚本(使用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

注意如何exit status报告,0即使exit /b 1应该如此1

现在很奇怪的是,如果我删除了innerif子句(这没关系,因为之后的所有内容exit /b 1都不应该执行),然后尝试启动它:

ok.cmd

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:\pf\mm_3.0.1\RendezVous\Services\Matchmaking>if "" == "" (
echo first if
 exit /b 1
)
first if
exit status: 1

现在exit status正确地报告为1

我不知道是什么原因造成的。嵌套if语句是否非法?

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

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


问题答案:

正如@dbenham指出的那样,“ [i] fa命令EXIT /B在同一命令块中的之后解析,即使随后的命令从不执行,问题也会显现出来”。在这种特殊情况下,IF语句的主体基本上被评估为

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

其中&运算符是函数cmd!eComSep(即命令分隔符)。的EXIT /B 1命令(功能cmd!eExit)是由全局变量设定评价cmd!LastRetCode为1,然后执行基本GOTO :EOF。当它返回时,第二个eComSepseescmd!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

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沿栈传递,成为流程退出代码。



 类似资料:
  • 我正在写一个批处理应用程序,我想在我的应用程序逻辑识别问题并返回时立即设置退出状态。 例如,我正在Writer的write方法中设置以下状态。

  • 我正在创建一个非常通用的Spring批处理应用程序,在该应用程序中,我从YAML文件中读取了大量配置,并动态地创建作业并注册如下所示的作业, 所有这些都是在Configuration类中完成的。之后,我会像下面这样启动所有的工作, 我从下面的另一个线程开始做, 这是可行的,但问题是它不会等待所有的作业,当它完成第一个作业时,应用程序将退出。我如何让这等到所有的作业完成,然后退出。

  • 为了Spark Streaming应用程序能够在集群中稳定运行,系统应该能够以足够的速度处理接收的数据(即处理速度应该大于或等于接收数据的速度)。这可以通过流的网络UI观察得到。批处理时间应该小于批间隔时间。 根据流计算的性质,批间隔时间可能显著的影响数据处理速率,这个速率可以通过应用程序维持。可以考虑WordCountNetwork这个例子,对于一个特定的数据处理速率,系统可能可以每2秒打印一次

  • ...there are dark corners in the Bourne shell, and people use all of them. Chet Ramey exit 命令一般用于结束一个脚本,就像C语言的exit一样。它也能返回一个值给父进程。 每一个命令都能返回一个退出状态(有时也看做 返回状态 ).一个命令执行成功返回0,一个执行不成功的命令则返回一个非零值,此值通常可以被解释

  • 问题内容: 我试图: 根据搜索条件查找文档, 如果找到,请更新一些属性 如果没有插入带有某些属性的文档。 我正在使用,因为我也在执行单个插入操作。我想在一次操作中完成所有操作。 但是,它什么都不会导致为更新/向上插入操作插入任何内容。 这是插入文档: 这是最新资料: 这就是我试图更新/更新的方式: 为什么不更新/更新呢? 注意 那是使用水线ORM的JS代码,它也使用mongodb本机驱动程序。 问

  • 问题内容: 与Linux中的类似,有没有一种方法可以获取Windows批处理文件()中程序的退出状态? 假设该程序在成功执行时失败,在失败时失败,我如何将这些退出值捕获在文件中? 问题答案: 使用%ERRORLEVEL%。您不喜欢批处理文件清晰明了吗?:)