web-dev-qa-db-ja.com

関数内からバッチファイルを終了するにはどうすればよいですか?

私はディレクトリをチェックするために書かれた簡単な関数を持っています:

: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で機能しないのですか?ご協力ありがとうございました!

14
Brad Conyers

ここでcallステートメントとgotoステートメントを混在させていると思います。

バッチファイルのラベルは、callまたはgotoで使用できますが、動作は異なります。
このような関数callの場合、関数がファイルの末尾に達したか、明示的にexit /bまたはgoto :eofgoto :endなど)に達したときに戻ります。

したがって、ラベルを関数として使用している場合は、バッチをキャンセルできません。

ただし、ラベルにgotoを指定しても、呼び出し元にはreturnされません。

synatxエラーの使用:

ただし、関数からバッチを終了する方法もあります。
構文エラーを作成できます。これにより、バッチが強制的に停止されます。
しかし、ローカル(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の回答を参照 関数内からバッチスクリプトを終了

高度な例外処理の使用:

これは、任意の量のスタックレベルを削除できるため、最も強力なソリューションです。現在のバッチファイルだけを終了し、スタックトレースを表示するためにも使用できます。
これは、引数なしで(goto)がスタックから1つの要素を削除するという事実を使用します。
参照 Windowsバッチは例外処理をサポートしていますか?

17
jeb

jebのソリューションはうまく機能します。ただし、すべての状況で適切であるとは限りません。これには2つの潜在的な欠点があります。

1)構文エラーはallバッチ処理を停止します。したがって、バッチスクリプトがスクリプトを呼び出し、スクリプトが構文エラーで停止した場合、制御は呼び出し元に返されません。それは悪いかもしれません。

2)通常、バッチ処理が終了すると、すべてのSETLOCALに対して暗黙のENDLOCALがあります。ただし、致命的な構文エラーはバッチ処理を終了します暗黙のENDLOCAL なし!これは厄介な結果をもたらす可能性があります: 詳細については、私のDosTipsの投稿---(SETLOCALがバッチ終了後も継続する! を参照してください。

Update 2015-03-20 参照 https://stackoverflow.com/a/25474648/101205 =すべてのバッチ処理を即座に終了するためのクリーンな方法。

関数内でバッチファイルを停止するもう1つの方法は、EXITコマンドを使用することです。これにより、コマンドシェルが完全に終了します。しかし、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ソリューションの潜在的な欠点の1つは、環境への変更が保持されないことです。これは、終了する前に環境を一時ファイルに書き込んでから、それを読み込むことで部分的に解決できます。

@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)が含まれている変数は正しく保持されません。

5
dbenham

exit /b Xを使用して関数を終了すると、ERRORLEVELXの値に設定されます。次に、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.
3
GrahamS

これがすべてのエラーレベルがチェックされている場合にネストされたルーチンをサポートする私の解決策です私はすべての私の呼び出し(内部または外部)でエラーレベルのテストを追加します

@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
1
Charles Godwin