在其他命令块中使用exit /b无法正常工作

6

我有一个批处理文件,需要在32位上下文中运行,因此包含一些代码来调用32位命令处理器并使用其自己的路径。此脚本还需要能够在失败时返回错误代码,它通过exit /b 123实现。然而...
exit()块中,并且包含任何语句时,这不会被正确地返回。

@echo off
setlocal EnableDelayedExpansion

rem Ensure we're running in 32-bit mode
if not %PROCESSOR_ARCHITECTURE%==x86 (
  echo Thunking to 32-bit mode
  %Windir%\SysWOW64\cmd.exe /c %0
  echo !ERRORLEVEL!
  exit /b !ERRORLEVEL!
)

(
  echo before
  exit /b 456
  echo after
)

输出结果如下:
H:\>sub.bat
Thunking to 32-bit mode
before
0

H:\>

如果在 exit 后面删除 echo,那么它将按预期正常工作。
H:\>sub.bat
Thunking to 32-bit mode
before
456

H:\>

即使你用rem或其他命令替换echo,它仍然失败。如果你手动运行32位的cmd.exe并运行相同的脚本,则退出代码会被正确设置。
H:\>sub.bat
before

H:\>echo %ERRORLEVEL%
456

H:\>

有人能解释并提供这个问题的解决办法吗?

"%Windir%\SysWOW64\cmd.exe /c %0 ^& exit %%%%errorlevel%%%%" 应该也可以工作(对Adam答案的补充说明)。 - JosefZ
1个回答

7
在脚本的“else”部分使用/b开关会导致第二个cmd.exe实例丢失退出代码。当从Syswow64执行批处理文件的第二个实例时,它将以456的代码退出。其父cmd.exe将接收此退出代码,但不感兴趣保留它。cmd.exe能够成功运行批处理文件,因此返回到第一个实例并以0退出。如果在脚本的“else”部分省略/b开关,则会强制批处理文件退出其父cmd.exe,而不仅是完成处理。由于告诉cmd.exe用456退出代码退出,因此这将被保留。exit的帮助文本确实暗示了这种行为,但除非你有这个想法,否则似乎不是很明显!

1
当从后面的 exit 中删除 /b 时,它会起到其他影响,例如如果直接从32位运行,则退出命令提示符以及任何调用批处理脚本。此外,第二个块通常在更多代码之后,并具有自己的条件,但在此示例中已被剥离。 - Deanna
3
很好的解释!+1(而且这是你在本论坛的第一个回答)欢迎!**:)** - Aacini
@Jason,方向错了,我们需要在非递归调用时使用/B而不是在递归调用时使用。不过这还是很容易做到的。为了最大限度地提高灵活性,环境变量可能更可取一些。个人建议无论当前命令处理器的位数如何,批处理文件始终在子进程中运行自身。 (您可以设置参数或环境变量以防止无限递归。) - Harry Johnston
1
你提到了一个else部分,但我在问题中找不到任何else关键字... - aschipfl
在我们另行讨论过之后,我还没有回答这个问题。我认为这种行为可能是一个错误或者延迟扩展的结果。 - Adam Piggott
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接