【问题标题】:How can I exit a batch file from within a function?如何从函数中退出批处理文件?
【发布时间】:2012-05-10 13:21:32
【问题描述】:

我编写了一个简单的函数来检查目录:

:direxist
if not exist %~1 (
    echo %~1 could not be found, check to make sure your location is correct.
    goto:end
    ) else (
    echo %~1 is a real directory
    goto:eof
    )

:end 写成

:end
endlocal

我不明白为什么程序在 goto:end 被调用后不会停止。我有另一个函数使用相同的方法来停止程序并且它工作正常。

:PRINT_USAGE
echo Usage:
echo ------
echo <file usage information>
goto:end

在这种情况下,程序在调用 :end; 后停止。为什么这在 :direxist 中不起作用?感谢您的帮助!

【问题讨论】:

标签: batch-file exit goto


【解决方案1】:

我想你在这里混合了callgoto 语句。

批处理文件中的标签可以与callgoto 一起使用,但行为不同。
如果你call 这样的函数,它将在函数到达文件末尾或显式exit /bgoto :eof(如你的goto :end)时返回。

因此,如果您将标签用作函数,则无法取消您的批次。

但是,goto 给一个标签,不会返回给调用者。

使用语法错误:

但也有一种方法可以从函数中退出批处理。
您可以创建语法错误,这会强制批处理停止。
但它有副作用,本地(setlocal)变量不会被删除。

@echo off
call :label hello
call :label stop
echo Never returns
exit /b

:label
echo %1
if "%1"=="stop" goto :halt
exit /b

:halt
call :haltHelper 2> nul

:haltHelper
() 
exit /b

使用 CTRL-C:

创建类似于 CTRL-C 错误代码的错误代码也会停止批处理。
退出后setlocal状态为clean
见@dbenham 的回答Exit batch script from inside a function

使用高级异常处理:

这是最强大的解决方案,因为它能够删除任意数量的堆栈级别,它可以用于仅退出当前批处理文件并显示堆栈跟踪。
它使用了 (goto) 不带参数从堆栈中删除一个元素这一事实。
Does Windows batch support exception handling?

【讨论】:

  • 除了无法返回调用批处理脚本之外,此解决方案还可以防止在批处理终止时应该发生的隐式 ENDLOCAL,从而导致不必要的延迟环境更改。
  • 请参阅stackoverflow.com/a/25474648/1012053,了解从正确终止 SETLOCAL 的 CALLed 例程中退出批处理的干净技术。
【解决方案2】:

jeb 的解决方案效果很好。但这可能并不适用于所有情况。它有两个潜在的缺点:

1) 语法错误将停止所有批处理。因此,如果批处理脚本调用了您的脚本,并且您的脚本因语法错误而停止,则控制权不会返回给调用者。那可能很糟糕。

2) 通常,当批处理终止时,每个 SETLOCAL 都有一个隐式 ENDLOCAL。但是致命的语法错误会终止批处理没有隐式ENDLOCAL!这可能会产生令人讨厌的后果 :-( 请参阅我的 DosTips 帖子 SETLOCAL continues after batch termination! 了解更多信息。

2015 年 3 月 20 日更新 请参阅 https://stackoverflow.com/a/25474648/1012053,了解立即终止所有批处理的干净方法。

在函数中停止批处理文件的另一种方法是使用 EXIT 命令,该命令将完全退出命令 shell。但是稍微创造性地使用 CMD 就可以帮助解决问题。

@echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
exit /b

:main
call :label hello
call :label stop
echo Never returns
exit /b

:label
echo %1
if "%1"=="stop" exit
exit /b

我的 PC 上既有名为“daveExit.bat”的版本,也有名为“jebExit.bat”的 jeb 版本。

然后我使用这个批处理脚本测试它们

@echo off
echo before calling %1
call %1
echo returned from %1

这是结果

>test jebExit
before calling jebExit
hello
stop

>test daveExit
before calling daveExit
hello
stop
returned from daveExit

>

EXIT 解决方案的一个潜在缺点是不会保留对环境的更改。这可以通过在退出之前将环境写入临时文件,然后再读回来部分解决。

@echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
for /f "eol== delims=" %%A in (env.tmp) do set %%A
del env.tmp
exit /b

:main
call :label hello
set junk=saved
call :label stop
echo Never returns
exit /b

:label
echo %1
if "%1"=="stop" goto :saveEnvAndExit
exit /b

:saveEnvAndExit
set >env.tmp
exit

但值中带有换行符(0x0A)的变量将无法正确保留。

【讨论】:

  • +1,也不错,而且你的优点是临时变量被移除了
  • @jeb - 我在想丢失环境变量设置可能是一个缺点。但我明白你的意思。在退出条件之前可能有任意数量的临时变量设置不应保留。如果有任何变量必须保留,它们可以选择性地写入临时文件并在 CMD 返回时恢复。
【解决方案3】:

如果你使用exit /b X 退出函数,那么它会将ERRORLEVEL 设置为X 的值。如果ERRORLEVEL 不为零,则可以使用|| conditional processing symbol 执行命令。

@echo off
setlocal
call :myfunction PASS || goto :eof
call :myfunction FAIL || goto :eof
echo Execution never gets here
goto :eof

:myfunction
    if "%1"=="FAIL" ( 
        echo myfunction: got a FAIL. Will exit.
        exit /b 1
    )
    echo myfunction: Everything is good.
    exit /b 0

此脚本的输出是:

myfunction: Everything is good.
myfunction: got a FAIL. Will exit.

【讨论】:

    【解决方案4】:

    这是我的解决方案,如果所有程序都检查了错误级别,它将支持嵌套例程 我在所有调用(内部或外部)中都添加了 errolevel 测试

    @echo off
    call :error message&if errorlevel 1 exit /b %errorlevel%<
    @echo continuing
    exit /b 0
    :error
    @echo in %0
    @echo message: %1
    set yes=
    set /p yes=[no]^|yes to continue
    if /i "%yes%" == "yes" exit /b 0
    exit /b 1
    

    【讨论】:

    • 我从你的帖子中学到了一些新东西。 %0 的回声是我从未接触过的东西(对于函数)。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 2012-12-25
    相关资源
    最近更新 更多