从子程序退出批处理文件


20

如何从子例程中退出批处理文件?

如果使用EXIT命令,则仅返回到调用子例程的行,然后继续执行。

这是一个例子:

@echo off
ECHO Quitting...
CALL :QUIT
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

输出:

Quitting...
Still here!

更新:

这不是一个正确的答案,但是我最终还是按照以下方式做点事情:

@echo off
CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL
ECHO You shouldn't see this!
GOTO END

:SUBROUTINE_WITH_ERROR
ECHO Simulating failure...
EXIT /B 1

:HANDLE_FAIL
ECHO FAILURE!
EXIT /B 1

:END
ECHO NORMAL EXIT!
EXIT /B 0

双管道声明:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

是以下内容的简写:

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

我仍然很想知道是否有一种方法可以直接从子例程中退出,而不必让CALLER处理这种情况,但这至少可以完成工作。


更新#2:当从另一个子例程中调用一个子例程时,如上所述,我从子例程中进行了调用:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

这样,错误可以传播回“主”,可以这么说。然后,批处理的主要部分可以使用错误处理程序GOTO:FAILURE处理错误。

Answers:


21

将此添加到批处理文件的顶部:

@ECHO OFF
SETLOCAL

IF "%selfWrapped%"=="" (
  REM this is necessary so that we can use "exit" to terminate the batch file,
  REM and all subroutines, but not the original cmd.exe
  SET selfWrapped=true
  %ComSpec% /s /c ""%~0" %*"
  GOTO :EOF
)

然后,您可以简单地调用:

  • EXIT [errorLevel] 如果要退出整个文件
  • EXIT /B [errorLevel] 退出当前子程序
  • GOTO :EOF 退出当前子程序

+1是实际提及的GOTO :EOF
Afrazier 2011年

1
非常好。我做了一个小的修改,分配%~0给变量而不是trueif not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... )。这样,您可以在相互调用的多个批处理脚本中使用相同的技巧。
GolezTrol 2015年

这是一个很好的解决方案。您认为值得对其进行解释以进行编辑吗?我花了一分钟的时间来解压缩所有内容,并意识到它实际上只是从嵌套的cmd.exe中调用%~0带有所有参数(%*)的批处理文件(),并且/s用于控制%ComSpec%参数处理围绕双引号的方式。呼叫。
肖恩

@Sean我发现简洁对大多数人来说更有用。自从我写这本书以来的7年里,一直没有更多文档被索要,因此,对它的需求似乎不大。我还认为,人们自己查找事物而不是欺骗/弄乱文档是有一些价值的。但是,也许还有更多人问我可以添加些东西。它也是CW,所以您也可以提出修改
Merlyn Morgan-Graham

3

这项小调整怎么样?

@echo off
ECHO Quitting...
CALL :QUIT
:: The QUIT subroutine might have set the error code so let's take a look.
IF ERRORLEVEL 1 GOTO :EOF
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

输出:

Quitting...

从技术上讲,这不会从子例程中退出。相反,它只是检查子例程的结果并从那里采取措施。


2
谢谢,那肯定可以完成工作,如果我找不到更好的答案,那就是我必须要做的。但是,我宁愿不必在冗长而复杂的批处理文件中的每个CALL之后粘贴该行。
布朗,

1

如果您不想从过程中回来,请不要使用call:,而是使用goto

@echo off
ECHO Quitting...
GOTO :QUIT
ECHO Will never be there!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

问题的关键是如何从子例程执行此操作(即使用调用),因此这无法回答。
Steve Crane

1

我将错误处理放入批处理文件中。您可以像这样调用错误处理程序:

CALL :WARNING "This is" "an important" "warning."

这是批处理文件的结尾:

::-------------------------------------------------------------------
::  Decisions
::-------------------------------------------------------------------
:INFO
IF "_DEBUG"=="true" (
  ECHO INFO: %~1
  IF NOT "%~2"=="" ECHO          %~2
  IF NOT "%~3"=="" ECHO          %~3
)
EXIT /B 0
:WARNING
ECHO WARNING: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
EXIT /B 0
:FAILURE
ECHO FAILURE: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
pause>nul
:END
ECHO Closing Server.bat script
FOR /l %%a in (5,-1,1) do (TITLE %TITLETEXT% -- closing in %%as&PING.exe -n 2 -w 1 127.0.0.1>nul)

1

这将退出当前上下文和父上下文(即,在一个call深层子例程脚本中执行时将退出):

(goto) 2>nul || exit /b

或者,如果您需要错误级别0:

(goto) 2>nul || (
    type nul>nul
    exit /b
)

基本上,(goto) 2>nul将errorlevel设置为1(不输出错误),将执行返回到父上下文,并在父上下文中执行双管道后将代码返回。type nul>nul将错误级别设置为0。

UPD:

要连续返回执行两次以上,请(goto) 2>nul ||像这样链接多个:

(goto) 2>nul || (goto) 2>nul || (goto) 2>nul || (
    type nul>nul
    exit /b
)

这是一个递归子例程,可以多次返回上下文:

:Kill
(goto) 2>nul || (
    set /a depth=%1-1
    if %1 GEQ 1 (
        call:Kill !depth!
    )
    (goto) 2>nul || (type nul>nul)
)

从递归函数调用时:

@echo off
setlocal EnableDelayedExpansion
call:Recurs 5
echo This won't be printed
exit /b

:Recurs
set /a ri+=1
echo %ri%
if %ri% LSS %1 (
    call:Recurs %1
)
echo This will be printed only once
call:Kill %1
exit /b

输出将是:

1
2
3
4
5
This will be printed only once
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.