バッチファイルの落とし穴 (一覧メモ)

特定のPowerShellスクリプトを呼び出して実行するバッチファイルを作成していた時にハマった落とし穴をメモ。

バッチ生成ツール

PowerShellスクリプト(.ps1)は設定を行わなければそもそもデフォルトで実行不可になっているうえ、バッチファイル(.bat)と異なりダブルクリックでの実行もできません。 実行が非常に不便なPowerShellスクリプトをどうにか[…]

↑のツールの作成時に色々と困ったので備忘録として以下に記載。バッチ難しい(小並感)

必読資料 (落とし穴の前に)

バッチのコマンド基礎文法

この資料がよくまとまっているので忘れたらこれを参照していた。

Qiita

改版履歴2010年 2月11日 初版2010年 2月13日 遅延環境変数展開を追加2010年 2月14日 糖衣構文を追加…

落とし穴対策

無限に落とし穴があるので、まず先にこれを読んでおくことを強く推奨。

Qiita

草「それ海外だとアウトローですぅ」「ここではない何処かに行きたいだけなら、それでもいいですけど(ドヤァ)」「プログラミン…

バッチファイルの特異的な仕様や落とし穴について例と対処法が沢山載っている。

(バッチじゃなくてもいいならみんな早くPowerShellにお引っ越ししよう…。)

以下、この資料の落とし穴以外でつまづいたところを説明。

バッチファイルの文字コードはShift_JISが前提!

バッチファイル.batの文字コードがShift_JISになっていない場合、日本語の文字列(全角文字)が正しく処理できない。

echo で表示しようとした日本語文が文字化けしたり、日本語を含むファイル名のファイルが呼び出せなかったりトラブルの原因になる。

(私の場合はバッチファイルから日本語名の.ps1ファイルが呼び出せず謎にハマりましたが、単にバッチファイルをutf-8の文字コードで保存してしまっていただけでした)

Shift_JIS でバッチを保存するか、バッチに文字コードを明記。

参考:UTF-8のバッチファイルが文字化けする時の対処3選 – なゆたり

ドラッグ&ドロップ時にカレントディレクトリが変更される

カレントディレクトリはバッチ実行場所とは限らない

バッチにファイルをドラッグ&ドロップすると、そのファイルのパスを引数として受け取った状態でバッチが起動する。

(バッチ中で、%1, %2,… を1番目、2番目…のファイルパスを表す特別な環境変数として使える)

ところが、バッチのカレントディレクトリがD&Dで渡したファイルが存在する場所に勝手に変更されてしまう(バッチのある場所がカレントディレクトリではなくなる)

例えばバッチに(別の場所の)ファイルAをD&Dしてバッチを起動した場合、以下の1行目と3行目で表示されるカレントディレクトリは異なる場所になる。

echo %CD%
cd /d %~dp0
echo %CD%

1行目:カレントディレクトリはファイルAがある場所

2行目でカレントディレクトリをバッチファイルがある場所に移動する。

3行目:カレントディレクトリはバッチファイルがある場所

つまりバッチファイルがあるディレクトリ「%~dp0」とカレントディレクトリ「%CD%」が一致しないことが発生しうる

D&D時はカレントディレクトリが変わるので、(必要に応じて)バッチがある場所に一度自分で変更する必要があり要注意!

(私の場合はバッチファイルから.ps1ファイルを相対パスで呼び出す処理を行っていたので、カレントディレクトリが変わると相対パスの起点が意図しない場所になって.ps1が呼び出せない問題が発生した)


コマンド補足

%CD%

%CD% はカレントディレクトリが格納されている特別な環境変数。

この手の、元から存在する特別な環境変数の一覧はこちら。(定義済み環境変数特殊環境変数)

  • 自前の環境変数はcamelCaseで、特別な環境変数は全て大文字で命名(表記)すると見やすいかも
  • %CD% は特殊環境変数の方。実行時に値が変わるような変数が特殊環境変数

 

cd /d %~dp0

cd

カレントディレクトリを変更するコマンド。後ろにパスを指定する。

/d

/d オプション:現在のドライブも変更

(※異なるドライブからファイルがドラッグ&ドロップされた時にも対応するために入れる)

%~dp0

このコマンドを実行しているバッチ本体のファイルがある場所を表す。

バッチ本体のフルパスが入っている環境変数 %0 に、変数の展開オプション ~dp が付けられている形。

%0

位置パラメーター。バッチファイル自体のフルパスがダブルクォーテーション付きで格納されている。

例: "C:\Workspace\sample1.bat"

~dp

変数展開のオプション。パス文字列を操作する書式設定みたいなもの。

  • チルダ「~」は文字列を囲むダブルクオーテーション(")を削除する書式。
  • 「dp」はファイルパス文字列からドライブレター(d)、ドライブレターとファイル名を除いたパス文字列(p)を取り出す書式設定の組み合わせ

つまり

%0 が "C:\Workspace\sample1.bat" なら

%~dp0 は C:\Workspace\

になる。

バッチを管理者として実行するとカレントディレクトリが変わる

バッチを管理者として実行するとカレントディレクトリは

C:\Windows\System32

になる。(バッチの存在場所とは関係なく)

カレントディレクトリがバッチの存在場所になっていないと困るという場合は、必ずカレントディレクトリをバッチの存在場所に変更するコマンドを入れるようにする。

( cd /d %~dp0 または pushd %~dp0 )

バッチに渡された引数を明示的に参照できるのは9個まで

%1~%9

バッチに渡された引数は %1~%9 の特殊な環境変数から参照できる。

(※詳しくはこちら(位置パラメーターの説明)を参照。)

ただし、引数が10個以上あっても %10 で10番目の引数は取り出せない。

(%1 と 0 に分解して解釈されてしまう罠)

%* は渡されたパラメータ全てを表す文字列になる

一方で、%* は10番目以降の引数も含めて全ての引数を並べた文字列になる。

例:渡した引数が hoge1 と hoge2 と hoge3 なら %* は

hoge1 hoge2 hoge3

になる。

shiftコマンドで引数を一つずらせる

shift コマンドを使うと 例えば %3 に入っていた引数が一つずれて %2 に格納される。(他も同様)

これなら10番目の引数も 1回shiftを使えば %9 に格納されるので取り出せる。

(詳しくは以下参照。)

リンク:バッチファイルで10個以上の引数を扱う - Qiita

環境変数%RANDOM%がランダムにならない罠

特別な環境変数 %RANDOM% には 0~32767 までの値のうちどれかが名目上ランダムに入っていることになっている。

で、実際には状況によっては全くランダムじゃなくなるので要注意。

バッチファイルを起動した時刻が秒単位で同じである場合、(乱数のシードがたかだか1秒単位なので)全く同じ乱数が生成するという罠。

詳細はこちらにまとめた。↓

バッチの罠

バッチファイル(・コマンドプロンプト)で乱数として使えると思っていた環境変数 %RANDOM% を使って罠にハマったというお話。 特別な環境変数 %RANDOM% には 0~32767 までの値のうちどれかが名目上ランダムに入っていること[…]

その他事項

※今後、バッチで落とし穴にハマり次第情報を追加していく予定。

基本的にはPowerShellを使っているから(特別な事情がない限り)バッチを使うことは今後無いかも。

記事化前の最新情報はこちらで先にツイートしています。サイト更新告知もこちら。