【问题标题】:Getting the exit code of an application started with the "cmd" and "start" commands使用“cmd”和“start”命令获取应用程序的退出代码
【发布时间】:2015-04-03 19:31:27
【问题描述】:

我有一个控制台应用程序。与此应用程序的交互是通过 TCP/IP 完成的。

我也有一个测试框架,它基本上是 BATCH 脚本的集合(...不是我的错)。这个测试框架对每个测试所做的基本上是这样的:

  1. start /min "myapplication.exe" 并等待,直到收到应用程序已启动并运行的验证。
  2. 通过 TCP/IP 向此应用程序发送命令,接收其回复,并检查时间和值是否与特定测试的预期相符。

我目前遇到的一个问题是应用程序由于某些内部错误而过早退出。我想区分失败的测试和应用程序崩溃。我对此的唯一指示是应用程序的退出代码。

所以,我尝试了以下方法:

start /min cmd /c "myapplication.exe || echo %errorLevel% > exitcode.txt"

然后在测试脚本中,

if exist exitcode.txt (
    set /p exitcode=<exitcode.txt
    echo ERROR: myapplication.exe returned exitcode %exitcode%.
    goto error
) else (
    goto do_processing
)

但由于某些奇怪的原因,文本文件从未出现,即使我有时会收到有关应用程序崩溃的对话框,即使我用已知的非零退出代码强行使其失败。测试只是通过do_processing 并且(当然)导致失败。

编辑 当我跑步时

start /min cmd /c "nonsense || echo %errorLevel% &gt; test.txt"

有时得到一个包含字符串 9009 的文本文件,但其他时候该文本文件包含字符串 0,或者有时是 1,...什么...?!

EDIT2 如果你输入

cmd /k "nonsense || echo %errorLevel%"

(注意/k 选项),您会在新窗口中看到0,但如果您随后输入echo %errorlevel%,您会得到1....

我知道批次不是很理智,但它至少应该是始终如一的疯狂...

对这里可能发生的事情有什么想法吗?

【问题讨论】:

  • 您必须使用 /wait 选项来获取退出代码。这应该首先打败使用 start 的点。当需要进行错误检查时,避免一劳永逸。
  • @Hans Passant:我认为这行不通,因为如果您使用start,退出代码将不会传递给调用脚本。您应该改用call。它的行为类似于start /wait,但传递变量。检查这个:stackoverflow.com/questions/13257571/…
  • @HansPassant: 但start /wait 表示脚本执行将阻塞。然后脚本就不能继续向它发出命令...

标签: windows batch-file console-application command-prompt exit-code


【解决方案1】:

%errorLevel%这样的正常扩展发生在语句被解析的时候,整个CMD /C命令行被一次性解析,所以你得到的值是命令运行之前存在的值(总是0)。

您可以在https://stackoverflow.com/a/4095133/1012053 获得更准确的解释。一开始可能很难理解和理解这些阶段的重要性,但值得付出努力。

要解决您的问题,您必须将变量的扩展延迟到您的 exe 运行之后。

你有两个选择:

选项 1) 使用 CALL 获得延迟的一轮扩展。

在批处理文件中,您可以将百分比加倍。但是使用 CMD /C 运行的命令是在命令行上下文下运行的,而不是批处理。将百分比加倍在命令行下不起作用。

相反,您必须在变量名中引入插入符号(cmd.exe 转义字符)。扩展的第一阶段发生在处理转义之前,因此它查找带有插入符号的名称,但没有找到。未找到时,命令行解析器会在未找到变量时保留原始文本。接下来处理特殊字符并使用转义符。所以当 CALL 轮的展开发生时,它会看到正确的变量名。

start /min cmd /c "myapplication.exe || call echo %^errorLevel% > exitcode.txt"

我相信您是在批处理脚本中发出 START 命令,因此您还必须将百分比加倍以防止父批处理脚本扩展 ERRORLEVEL。

start /min cmd /c "myapplication.exe || call echo %%^errorLevel%% > exitcode.txt"

选项 2)使用延迟扩展

延迟扩展语法是!errorlevel!,而不是%errorlevel%。但在使用它之前,必须启用延迟扩展。在批处理脚本中,您将使用setlocal enableDelayedExpansion,但这在命令行上下文中不起作用。相反,您必须使用 cmd.exe /v:on 选项。

假设您的批处理脚本没有启用延迟扩展,那么您只需使用以下内容:

start /min cmd /v:on /c "myapplication.exe || echo !errorLevel! > exitcode.txt"

但是如果您的批处理脚本启用了延迟扩展,那么您必须转义!,以便父批处理脚本不会扩展ERRORLEVEL。请注意,您仍然必须使用/v:on,因为已启动的子进程(通常)默认禁用延迟扩展。

start /min cmd /v:on /c "myapplication.exe || echo ^!errorLevel^! > exitcode.txt"

【讨论】:

  • 啊啊啊啊!我不敢相信我又一次沦陷了!!这很好地解决了它,非常感谢。呃,想想这给这么多人带来的头痛......
  • 只是出于好奇:callcmd /v:on 相比有什么优势/劣势吗?
  • 在这种情况下,不是真的。 CALL 相对来说非常慢,但只有在紧密循环中进行多次迭代时才会发挥作用——这里不是问题。 CALL 还会对应该通过的引用插入符造成严重破坏(将它们加倍),这也不是问题。延迟扩展会破坏值包含 ! 字符的 FOR 变量的扩展,这在这里也不是问题。
  • 事情仍然没有按预期进行......当情况发生(应用程序崩溃)时,文本文件将写入您提供的两个选项中。但是,对于选项 1),它包含“ECHO is on.”,对于选项 2),它包含“0”...不确定是否重要,但 start 是从 if (...) 中调用的,它驻留在一个批处理文件中,它是来自另一个批处理文件的called,它具有setlocal EnableDelayedExpansion
【解决方案2】:

另一种解决方案可能是使用&amp; 而不是||

start myapplication.exe ^& echo %errorLevel% ^> exitcode.txt

^ 是一个转义字符,因此它是在开头内部而不是外部评估的,正如 here 所解释的那样。

这对我有用。希望它可以帮助某人。

【讨论】:

    【解决方案3】:

    正如here 解释的那样,您必须使用call 而不是start 才能评估退出代码。 call 将在同一变量环境中启动脚本,而 start 将在第一个脚本无法访问的新环境中运行它。

    【讨论】:

    • ...但是退出代码是在同一个进程中写入一个file的,所以不用关心具体的变量环境...跨度>
    • 您可以使用@echo %errorlevel% &gt;&gt; output.txt 编写退出代码。这就是你不能使用start 的原因。 start 不会在调用脚本中设置可访问的 %errorlevel%。使用start 可以让您在后台执行测试,但您永远无法访问退出代码。
    • 哦,对不起,我误会你了。
    • 这就是我使用start /min cmd /c "myapplication.exe || echo %errorLevel% &gt; exitcode.txt" 的原因。然后启动一个cmd,它有自己的变量环境,它运行应用程序并(有条件地)将任何非零退出代码输出到文件。
    • 是的,抱歉,原标题可能有误导性。
    猜你喜欢
    • 2010-09-24
    • 2018-11-04
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    相关资源
    最近更新 更多