お役立ち度0%?のコマンドシェル情報

はじめに

昔書いたバッチファイルとシェルスクリプト(sh系)の比較情報を見つけたので、役に立たないなと思いつつネットに晒してみます。

カレントディレクトリの取得

カレントディレクトリの取得方法は次のとおりです。

バッチ シェル
環境変数 CD で取得 環境変数 PWD で取得

空行の出力

ログを見栄え良く出力するために空行を出力する場合は、次の方法を用います。

バッチ シェル
ECHO の直後にドット(.)を記述 echo のみを記述

算術式の評価

コマンド実行回数のカウントアップやカウントダウンなどの簡単な演算を行いたい場合は、次の方法を用います。

バッチ シェル
SETコマンドを /A オプション付きで実行 exprコマンドを実行

日付と時刻の取得

実行ログの名称に日付や時刻を含めることがよくあります。
日付と時刻の取得方法は次のとおりです。

バッチ シェル
dateコマンドのフォーマット指定を使用 環境変数DATEとTIMEを使用

注意(バッチ): 環境変数DATEとTIMEは同時に取得できないので、上記の方法では厳密には23:59:59から00:00:00に切り替わる瞬間でDATEが一日前の日付になってしまうことがあります。

デフォルト値の利用

環境変数にデフォルト値を採用しておくと、上書きしたい値だけを設定しなおせば良くなるので便利です。
デフォルト値を使用する方法は次のとおりです。

バッチ シェル
IF NOT DEFINED を使用 ${parameter=word}構文を使用

標準入力からの入力待ち

標準入力からの入力を待ち、入力されたデータを変数に設定する方法は次のとおりです。

バッチ シェル
SETコマンドを /P オプション付きで実行 readコマンドを実行

標準出力と標準エラー出力の切り分け方法

標準出力と標準エラー出力を切り分ける方法は次のとおりです。

バッチ シェル
標準出力をファイルに出力する場合は、> または 1> を使用
標準エラー出力をファイルに出力する場合は、2> を使用
両方を同じファイルに出力する場合は、2>&1 または 1>&2 を使用
同左

標準出力または標準エラー出力の破棄

標準出力または標準エラー出力を破棄する方法は次のとおりです。

バッチ シェル
nulに出力 /dev/nullに出力

AND条件とOR条件

AND条件とOR条件の記述方法は次のとおりです。
ただし、Windowsの場合はあくまでもAND条件とOR条件を同等の表現方法で扱うときの記述方法です。

バッチ シェル
環境変数を結合した文字列を使って条件式を記述 -a または -o を使って条件式を記述

注意(バッチ): 環境変数 EA に値 VAVA が設定されていた場合でも、EA == VA が成立してしまいます。

AND条件しか書かない場合は、次の方法を採用するべきです。

バッチ シェル
環境変数を結合した文字列を使って条件式を記述 -a または -o を使って条件式を記述

if-else if-...-else構文

if-else if-...-else構文の記述方法は次のとおりです。

バッチ シェル
IF (...) ELSE IF (...) ELSE (...)と記述 if ... then elif ... then else ... fi と記述

終了ステータスの取得方法

実行したコマンドの終了ステータスを取得する方法は次のとおりです。

バッチ シェル
ERRORLEVELを使用 $?を使用

注意(バッチ): 「IF ERRORLEVEL <数値>」形式の構文も存在します。この構文を用いた場合、終了ステータスが「<数値>以上」のときに判定結果が真になるので、特定の値と等しいこと/等しくないことを判定することができません。

終了ステータスのリセット方法

終了ステータスをリセットする方法は次のとおりです。

バッチ シェル
date /t > nulなどを実行 特別な処理は不要

補足(バッチ): UNIXのコマンドインタプリタの場合は、setなどのビルトインコマンドであっても、処理が正常終了したのか、異常終了したのかに関わらず、行儀良く終了ステータスを変更するようになっています。しかし、Windowsのコマンドインタプリタの場合は、setなどのビルトインコマンドが正常終了した場合に、終了ステータスを変更してくれないことがあります。

終了ステータスの簡略判定方法

処理を終了するときなどに、毎回if文を記述して終了ステータスを判定するのは煩わしいだけでなく、バッチファイルやシェルスクリプトを読みにくくする原因になります。
読みやすさを保ちつつ、終了ステータスを適切に扱うための簡略記法は次のとおりです。

バッチ シェル
コマンドの後ろに && または || を記述 コマンドの後ろに && または || を記述

&&を使用した場合、&&より前のコマンドが終了ステータス 0 で終了した場合に、&&より後のコマンドが実行されます。
||を使用した場合、||より前のコマンドが終了ステータス 0 で終了しなかった場合に、||より後のコマンドが実行されます。

サブルーチンの作成方法

処理を共通化してサブルーチン化する場合は、次の方法を用います。

バッチファイルの場合
REM サブルーチンの定義方法
:サブルーチン名
処理を記述...(%1, %2, ...でパラメータを取得)
EXIT /B 終了ステータス

REM サブルーチンの呼び出し方法
CALL :サブルーチン名 パラメータ1 パラメータ2
シェルスクリプトの場合
# サブルーチンの定義方法
サブルーチン名() {
  処理を記述...($1, $2, ...でパラメータを取得)
  return 終了ステータス
}

# サブルーチンの呼び出し方法
サブルーチン名 パラメータ1 パラメータ2 ...
バッチ シェル

例では、2つのパラメータを受け取るサブルーチンを定義しています。
パラメータ1が0を受け取った場合は、標準出力に何も表示せずに終了ステータス0でサブルーチンを終了します。
パラメータ1が0以外を受け取った場合は、標準出力にNGと表示し、終了ステータスにパラメータ2の値を設定してサブルーチンを終了します。

全体処理の終了方法

処理が正常な状態であるのか、異常な状態であるのかに関わらず、バッチファイルやシェルスクリプトは終了ステータスを明示して全体の処理を終了するべきです。
特に、バッチファイルでは、「GOTO :EOF」でバッチファイルの末尾にジャンプして、バッチ処理を事実上終了することもできますが、終了ステータスが曖昧になるのでお勧めできません。
終了ステータスを明示して終了する方法は次のとおりです。

バッチ シェル
/B付きでexitコマンドを実行 exitコマンドを実行

注意(バッチ): /B オプションなしでEXITコマンドを実行した場合、バッチを起動しているコマンドインタプリタごと処理が終了してしまいます。
これはコマンドプロンプトで作業している場合、そのコマンドプロンプトも閉じられるということを意味します。
終了ステータスを指定せずにEXITコマンドを実行した場合、バッチファイルまたはシェルスクリプトの終了ステータスは、最後に実行したコマンドの終了ステータスになります。

サブルーチンの終了方法

サブルーチンを終了する方法は次のとおりです。

バッチ シェル
/B付きでexitコマンドを実行 returnコマンドを実行

別ファイルを呼び出す方法

別ファイルに記述されているバッチファイルまたはシェルスクリプトを呼び出す方法は次のとおりです。

バッチ シェル
CALLコマンドを使用 . を使って取り込み

注意(バッチ): CALLコマンドは指定されたバッチファイルを呼び出すだけで、呼び出されたバッチファイル内のサブルーチンを取り込むことはできません。
注意(バッチ): CALLされる側のバッチファイルは、CALLされる側に処理を戻すために、必ずEXIT /Bで処理を終了する必要があります。
補足(シェル): シェルスクリプトの場合は、ファイル内のサブルーチンを取り込むことができます。

コマンド実行結果の取得

シェルスクリプトの場合は、コマンドをバッククォート(``)で囲んでコマンドの実行結果を取得できますが、バッチファイルの場合は、この方法ではコマンドの実行結果を取得できません。
しかし、実行結果を取得する方法が全く無いわけではありません。シェルスクリプトに比べて適用範囲や記述の柔軟さは圧倒的に劣りますが、次の方法でコマンドの実行結果を取得することができます。

バッチ シェル
サブルーチンを作成してコマンドの実行結果を取得 バッククォートを使用してコマンドの実行結果を取得

補足(バッチ): 第1パラメータに実行結果を受け取る環境変数名(DATETIME)、第2パラメータに実行したいコマンド(date /t)を与えてサブルーチンを呼び出しています。

Windowsの遅延環境変数(SETLOCAL ENABLEDELAYEDEXPANSION)

文字列置換

遅延環境変数の展開を併用すると、0埋めした文字列を簡単に作成することができます。

例では、数値の先頭を0埋めした3桁の文字列を作成しています。

変数の評価タイミング

UNIXのコマンドインタプリタは、コマンドの実行時に変数を展開しますが、Windowsのコマンドインタプリタはコマンドを囲む文を評価するタイミングで変数を展開してしまいます。
UNIXのコマンドインタプリタに近いタイミングで変数を展開させる場合は、遅延環境変数の展開を有効にします。

バッチ シェル

バッチファイルでは、1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 を計算するつもりで処理を記述していますが、最初のECHO %SUM% の結果は、期待に反して 10 になります。
これは、Windowsのコマンドインタプリタが、FOR文を実行する前に %〜% で囲まれた環境変数の値を展開し、SET /A SUM=%SUM% + %%i を SET /A SUM=0 + %%i に置き換えてからFOR文を実行するからです。
これに対して、2番目の ECHO %SUM% の結果は期待通り 55 になります。
これは、「SETLOCAL ENABLEDELAYEDEXPANSION」を使用して、遅延環境変数の展開を有効にした上で、環境変数を %SUM% ではなく、 !SUM! で参照しているからです。
!SUM! と記述すると、FOR文を実行する前ではなく、環境変数を参照するタイミングで環境変数の値が展開されます。

遅延環境変数の展開の有効/無効状態判定

バッチファイルを記述する際に、「SETLOCAL ENABLEDELAYEDEXPANSION」が設定されているという前提で処理を記述したい場合があります。
CALLコマンドを用いて、あるバッチファイルから別のバッチファイルを呼び出す場合が、このケースに該当します。
この場合は、バッチファイルの先頭に次のようなコマンド列を記述することで、「SETLOCAL ENABLEDELAYEDEXPANSION」が設定されていないときにバッチファイルの実行を中止することができます。

例では、「SETLOCAL ENABLEDELAYEDEXPANSION」が無効になっていると判断した場合に、終了ステータス1でEXITコマンドを実行し、呼び出し元に戻ります。

おわりに

項目の順番は気が向いたら直したいと思いますが、ひとまず列挙してみました。また、乱文になっていると思いますが平にご容赦を。