退出嵌套批处理文件

8
我有4个批处理文件,假设它们是a.bat、b.bat、c.bat和d.bat。现在这些批处理文件是按照这样的方式调用的:a.bat调用b.bat,b.bat调用c.call等等。
如果任何一个批处理文件出现错误,我想通过显示一个错误消息并指出哪个批处理文件出现了问题来退出整个程序。我的问题是:我该如何做到这一点?
在这里,我使用了exit /b,但它只能从当前批处理文件中退出,并返回到调用它的批处理文件:
a.bat
@echo. off
echo. this is batch 'a'
call b.bat

b.bat

@echo. off
echo. this is batch 'b'
call c.bat

c.bat

@echo. off
echo. this is batch 'c'

我在批处理'C'中遇到了错误 - 然后应该报告错误并退出,但它不知何故又返回到批处理'B'。有没有办法从嵌套的批处理文件中退出呢?

5个回答

5
你可以利用语法错误来停止批处理,而不关闭命令窗口。 :HALT函数调用:__halt函数只是为了抑制错误信息。 c.bat
@echo off
echo this is batch 'c'
echo An error occurs
call :HALT
exit /b

:HALT
call :__halt 2> nul
exit /b

:__halt
()

你可以不需要额外的 :HALT,在XP系统中直接调用 :__halt 2>nul 即可,没有错误信息。 - Endoro
1
@Endoro 显然,但我想提供一个“HALT”函数,可以在不需要任何额外文本的情况下调用。 - jeb
聪明的解决方案加1分。然而,我认为如果您知道脚本在失败点,它应该优雅地退出并提供有意义的输出。 - David Ruhmann
1
这种巧妙的技术有一个不幸的副作用 - 在致命的语法错误之后,SETLOCAL无法被正确终止。请参见[我的答案](https://dev59.com/sHTYa4cB1Zd3GeqPvHgN#25475104),其中介绍了另一种更干净地终止批处理的方法。 - dbenham

2
你可以尝试使用以下命令(c.bat):
@echo. this is batch 'c'
@pause
exit

1
是的,但 OP 可以调用批处理 cmd /c a.bat。这不会关闭窗口。但你的方法也可以。 - Endoro

2
你可以使用退出代码和错误级别,具体描述请参见此链接:http://www.robvanderwoude.com/errorlevel.php 特别是,如果你想手动引发错误,则 c.bat 或 b.bat 应该显式指定退出代码。
EXIT /b 1

(或您选择的任意数字),但如果您只想让Windows自动错误计数,则可以在运行b.bat或c.bat之后,直接编写以下内容。

IF ERRORLEVEL 1 EXIT /b %ERRORLEVEL%

这将把相同的错误传播到下一个程序,因此如果您愿意,它们可以立即退出。优点是您可以随时停止向上传播。

(编辑:明确指出,在使用手动或自动错误时,除了底层程序外,这里提到的第二行代码在所有程序中都是必需的)


这不会打破链条。 - Endoro
很抱歉,我不理解你和jeb认为他在询问什么的用例,因此,我会给出我认为他在询问的答案,即“我希望b.bat在c.bat出错后停止运行其余指定的代码,并且我希望a.bat对b.bat执行相同操作”。如果我误解了他的意思,他显然会接受你们其中一个人的答案而不是我的。 - codetaku
从这个问题来看,“我在这里使用了exit /b,但它只能退出当前的批处理文件并返回到调用它的批处理文件中”。我对其合理使用没有深入了解。这可能是可以实现的,但超出了我的范围。调节不是我的部门。 - Endoro

2

jeb的致命语法错误方法可以使用,但它不能正确终止任何活动的SETLOCAL。

例如,这是fatalTest.bat:

@echo off
setlocal enableDelayedExpansion
set test=AFTER main SETLOCAL
call :sub
echo returning from main  NEVER REACHED
exit /b

:sub
setlocal
set test=AFTER sub SETLOCAL
set test
call :ExitBatch
echo returning from sub2 NEVER REACHED
exit /b


:ExitBatch
call :__exitBatch 2>nul
:__ExitBatch
for

以下是演示运行输出,证明测试仍然被定义并且批处理终止后可启用延迟扩展:

C:\test>fatalTest
test=AFTER sub SETLOCAL

C:\test>echo !test!
AFTER sub SETLOCAL

C:\test>

现在已经不可能将环境恢复到批处理之前的状态。CMD.EXE会话应该被终止。


这是一个改进后的CtrlCTest.bat版本,它可以在批处理结束时正确地终止任何活动的SETLOCAL。它使用了一种令人惊讶的技术来编程"按下"Ctrl-C,我在为什么非交互式批处理脚本认为我按下了control-C?中学到了这个技巧。

@echo off
setlocal enableDelayedExpansion
set test=AFTER main SETLOCAL
call :sub
echo returning from main  NEVER REACHED
exit /b

:sub
setlocal
set test=AFTER sub SETLOCAL
set test
call :ExitBatch
echo returning from sub2 NEVER REACHED
exit /b


:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b

以下是示例运行输出。您可以看到延迟扩展不再处于活动状态,test也不再被定义:

C:\test>CtrlCTest
test=AFTER sub SETLOCAL

C:\test>echo !test!
!test!

C:\test>set test
Environment variable test not defined

C:\test>

您可以将:ExitBatch例程放入一个独立的ExitBatch.bat脚本中,该脚本位于您的PATH路径中的某个位置,然后任何批处理都可以方便地在需要时调用该实用程序。
@echo off
:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b

0

如果.BAT文件在TakeCommand下,请使用CANCEL。


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