バッチファイルで関数は定義できませんが、call でラベルにジャンプすることで、サブルーチンとして実行できます。
call :ラベル名
goto でジャンプしてしまうと、ジャンプ先のコマンド群を実行した後に戻ってきてくれませんが、call でジャンプすれば、呼び出し位置に戻ってきてくれます。
下記の例では、関数もどきとして、MyFunc1、MyFunc2 を定義し、呼び出しています。
@echo off
REM === メインシーケンス
call :MyFunc1
call :MyFunc2
exit /b
REM === 関数 1
:MyFunc1
echo Hello, I am MyFunc1
exit /b
REM === 関数 2
:MyFunc2
echo Hello, I am MyFunc2
exit /b
C:\> hello
Hello, I am MyFunc1
Hello, I am MyFunc2
それぞれのシーケンス(メイン、関数1、関数2)の最後で、exit /b を実行しなければいけないことに注意してください。
そうしないと、それぞれのシーケンスで、バッチファイルの最後まで実行してしまいます(その場合も call 呼び出し位置に戻ってきます)。
サブルーチンに渡されたパラメータは、%1、%2 などで取得することができます。
@echo off && setlocal
REM === メインシーケンス
call :MyFunc aaa
call :MyFunc aaa bbb ccc
call :MyFunc "aaa"
call :MyFunc "aaa bbb ccc"
exit /b
REM === 関数定義
:MyFunc
echo %1
exit /b
C:\> sample
aaa
aaa
"aaa"
"aaa bbb ccc"
パラメータ内にスペースや特殊な記号を含む場合は、ダブルクォーテーション " で囲めばよいのですが、%1 で取得した結果にダブルクォーテーションが含まれてしまいます(上記サンプルの "aaa bbb ccc" という出力結果)。
ダブルクォーテーションを削除した結果を取得したい場合は、以下のように %~1(チルダ付き)で参照します。
REM === 関数定義
:MyFunc
echo %~1
exit /b
C:\> sample
aaa
aaa
aaa
aaa bbb ccc
バッチファイル内で変数を定義するときに、あらかじめ SETLOCAL を呼び出しておくと、変数のスコープがサブルーチンやバッチファイル内に限定されます。
つまり、SET 変数名=値 で定義した変数が、ローカル変数のように振る舞うようになります(デフォルトではグローバル変数のように振る舞います)。
SETLOCAL した後で ENDLOCAL を呼び出すと、SETLOCAL を呼び出したときの変数定義状態に戻すことができます。
ただ、サブルーチンから戻る際は自動的に ENDLOCAL されるようになっているので、実際にはサブルーチンの先頭(ラベル名の直後やバッチファイルの先頭)で SETLOCAL しておくだけで十分です。
@echo off && setlocal
set price=100
echo %price%
call :MyFunc
echo %price%
exit /b
:MyFunc
setlocal
set price=200
echo %price%
exit /b
100
200
100
ちなみに、call コマンドでは、別ファイルのバッチファイルを呼び出すこともできます。
サブルーチンの呼び出しでは、先頭にコロン : を付けますが、この場合は代わりにバッチファイル名を指定します。
call %~dp0SubModule.cmd (拡張子 .cmd は省略可能)
先頭の %~dp0 は、バッチファイルの含まれているディレクトリを表すおまじないです(参考: バッチファイルのファイル名やディレクトリ名を取得する)。
これを入れることにより、バッチファイルのあるディレクトリ以外から実行した場合に、正しく別のバッチファイルを参照できるようになります。
ちなみに、呼び出されるバッチファイル側は exit /b で終了する必要はありません。
次の例では、バッチファイル Main から、バッチファイル SubModule を呼び出しています。
SubModule の処理が終わると、Main の続きから処理が再開されます。
@echo off && setlocal
set my_name=Main
echo I am %my_name%
call %~dp0SubModule
echo I am %my_name%
@echo off && setlocal
set my_name=SubModule
echo I am %my_name%
C:\> Main
I am Main
I am SubModule
I am Main
call で別のバッチファイルを呼び出すときに渡した引数は、%1、%2 のように参照することができます。
実際には、前述したとおり、ダブルクォーテーションを外した値を取得するために、%~1%、%~2% のようなチルダ付きで参照します。
@echo off && setlocal
call %~dp0hello Maku
call %~dp0hello Ponyo
call %~dp0hello "Chiko Rita"
@echo off && setlocal
echo Hello, %~1%
C:\> main
Hello, Maku
Hello, Ponyo
Hello, Chiko Rita