遠藤ヒズミの blog

息を吐くが如く更新が最終目標。技術からガジェット、ニチアサなどジャンルは様々

キレない関数(サブルーチン)の作り方~バッチファイル編~

はじめましての方は、初めまして
ご存知の方は、そうだよヒズミさんだよ

今回は、みんな大好きバッチファイルの話だよ。
バッチファイルは、IFやFORといった制御構文を持ち、変数による値の保持ができます。
そのため、ただ、コマンドを羅列するだけではなく、ある程度のスクリプトを書くことができます。
ただし、やや癖があるので、ほかの言語と同じ感覚でうっかりサクッと手を出すと、簡単にハマります。
ストレスで毛根がマッハで抜けます。
そうならないための記事の序章がこちら

endohizumi.hatenablog.com

バッチファイルの関数について

概要みたいなもん

バッチファイルは、前述の通り、IFやFORの制御構文の他に、関数のようなものを宣言することができます。 スクリプトの中で、再利用する処理をまとめることができます。 実際は、ジャンプ先の目印をつけるだけのラベルというのですが、ここでは、便宜上、関数と呼称します。

宣言の仕方

:sayHello
echo Hello, world
exit /b

コロン(:)の後に、名前を記述して改行することで、宣言したことになります。 bashスクリプトを書いたことある人は、以下の様に、functionの接頭辞をつけたと思いますが、 前述の通り、ジャンプする行の目印をつけているだけなので、名前だけで十分です。
呼び出し元に戻るには、exit /bを使います。
/bはオプションなので、/b無しのexitと記述しても構文どおりでエラーは起きません。
その代わり、コマンドプロンプト自体が終了します。

function hogehoge(){
 echo hogehoge
}

呼び出し方

宣言した関数の呼び出し方は、以下のように、callの引数に関数名を指定すると、呼び出すことができます。

call :sayHello
exit /b

:sayHello
echo Hello, world
exit /b

関数に対して、引数を渡す場合は、CLIアプリケーションに引数を渡すように関数名の後ろに半角スペースをつけるだけです。

call :say HelloTaro
exit /b

:say
echo %1
exit /b

gotoとの違い

似たようなコマンドに、指定の箇所に飛ぶ gotoというものがあります。
どちらも指定ラベルにジャンプするコマンドですが 関数として、指定ラベル先にジャンプするcallと違い、gotoは指定ラベル先にジャンプした後、呼び出し元に戻ることはありません。
関数として呼んだわけではないので、引数を指定しても渡されることもありません。

goto :say HelloTaro
echo hogehoge
exit /b

:say
echo %1
exit /b

実行結果
f:id:endo_hizumi:20191026170956p:plain

戻り値

実際のところ、関数ではないので、戻り値も何もありませんが・・・
call で呼び出した場合、exit /b の後に終了時の状態を数値で返すことができます。
CLIアプリケーションを使ったことある人は、0とか1とかコマンド実行後に返されると思います。
それを自分で決めることができます。バッチファイルの外に出すなら、連携のことも考えて、0か1を返すのが無難ですが、バッチファイルの中ならマイルールで返しても、大丈夫です。
関数が返した数値は、ERRORLEVEL変数で、参照できます。

@echo off

CALL :say helloTaro
echo %ERRORLEVEL%
exit /b

:say
echo %1
exit /b 5

実行結果
f:id:endo_hizumi:20191026172151p:plain

関数内でローカル変数を宣言する

関数内に限った話ではないのですが。 バッチファイルは、基本的に宣言した変数はグローバルです。 しかし、setlocalコマンドを使うと、それ以降の行はendlocalコマンドを使うまでローカル変数扱いになります。

@echo off

setlocal
set hoge=fugafuga
rem fugafugaが表示される。
echo %hoge%
endlocal
rem 外なので、表示されない
echo %hoge%

f:id:endo_hizumi:20191026174949p:plain

注意

他の言語と違い、あくまでもジャンプ先の目印をつけただけなので、工夫しないと関数の中の処理も行ってしまうので、注意してください。

rem こう書くと、echo hogehogeを実行する前に、終了する。

:sayHello
echo Hello, world
rem ここで、処理が終わる
exit /b

echo hogehoge
rem こう書くと、echo hogehogeとecho Hello,worldを実行する

echo hogehoge

rem 処理は終了していないので、これ以降の処理が実行される。
:sayHello
echo Hello, world
exit /b
rem こう書くと、echo hogehogeだけを実行する

echo hogehoge
exit /b

:sayHello
echo Hello, world
exit /b

参考