15

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

: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 中不起作用?感谢您的帮助!

4

4 回答 4

18

我想你在这里混合callgoto陈述。

批处理文件中的标签可以与 acall或 a一起使用goto,但行为不同。
如果您使用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 状态是干净的!
请参阅@dbenham 的回答Exit batch script from inside a function

使用高级异常处理:

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

于 2012-05-10T13:44:45.903 回答
5

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

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

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

更新 2015-03-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”,jeb 的版本名为“jebExit.bat”。

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

@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) 的变量将无法正确保留。

于 2012-05-10T15:33:17.067 回答
4

如果您使用exit /b X退出函数,那么它将设置ERRORLEVEL为 的值X。然后,如果不为零,您可以使用|| 条件处理符号来执行命令。ERRORLEVEL

@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.
于 2017-04-06T10:34:19.053 回答
1

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

@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
于 2013-01-08T19:29:50.093 回答