Windowsバッチスクリプト記述法メモ
errorlevelでなくERRORLEVELだ!!!!(2018/04/13)
メモです。
- バッチファイルの雛形:戻り値、カレントフォルダ、pause
- 他の.batを呼ぶときはcallを使う
- コメントは::で
- 改行(長いコマンド行を分ける)
- echo
- 標準出力と標準エラーをまとめてリダイレクト
- 複数ファイルを指定してcopyする
- xcopyが無い?xcopyではフォルダコピーができない?
- フォルダのデリートはdelではない
- 無意味なメッセージの抑止(NULLでなくNUL)
- for制御、変数問題回避、callとgoto :eof
- if制御、変数問題回避
- 変数のファイル名取得修飾
次の観点で作成したバッチファイルの雛形です。
- 結果を呼び出しもとにERRORLEVELとして返す
- カレントフォルダをバッチファイルのあるフォルダにする
- クリックされて起動した場合はpauseする。他ファイルから 引数付きで起動された場合はpauseしない
"exit /b 変数"で返した値は呼び出し元でERRORLEVELで参照できます。 /bを忘れると呼び出し元に戻らず終了してしまうので注意が必要です。 ERRORLEVELに直接値を入れても期待する当たり前の動作はしないので 注意が必要です。@echo off pushd %~dp0 ::------------------------- ここに動作 ::------------------------- :OK set result=0 goto END :ERR set result=1 :END if not "%1"=="" goto NOPAUSE pause :NOPAUSE popd exit /b %result%
標準のERRORLEVELは例えばtypeコマンドなどでも変化してしまいます ので通常は状態を別変数にとりexit /bすべきです。仮にERRORLEVEL をそのまま返したい場合でもset result=%ERRORLEVEL%などとした 上でresultを返す習慣をつけるのが安全です。
pause制御は単純ですが、色々な部品から組み上げられるシステムで、部分部分で
起動可能で、かつ、全体を統括するバッチからも起動可能となります。引数のない
バッチの呼び出しには擬似引数(便宜上nopauseとする)を付加します。
バッチは直接書き下ろさず、必ずこういった雛形を使うべきです。
バッチファイルから他のバッチファイルを呼ぶことはできますが、callを 使わない場合、信じられないことに、戻って来ません。
callで別フォルダのbatを呼び出した場合、カレントフォルダは通常は呼び出し元のまま です。通常はフォルダをまずpushdで変更した上でcallします。戻った後直ぐpopdで 戻します。popdではERRORLEVELは変化しません。
なお、ERRORLEVELのチェックには==などは使いません。if errolevel 1で正解です。@echo off pushd %~dp0 pushd dir1\dir2\dir3 call test1.bat nopause popd if ERRORLEVEL 1 goto ERR :OK set result=0 echo === OK === goto END :ERR set result=1 echo === SOME ERROR OCCURED === :END @if not "%1"=="" goto NOPAUSE pause :NOPAUSE popd exit /b %result%
コメントは"::"で始めます。
:: コメント
これは本来はラベルなのですが、コメントとして利用できます。
本来のコメント文はREMですが、REMは極めて視認性が悪いため
使うべきではありません。
REM コメントだが余りにも見づらい
長いコマンド行を分けるには分ける部分に^を置きます。
java -jar ..\..\lib\symphonie.jar syntax-check ^ -syntax syntax.hn -source e2.txt -xml -with_begin_end -out out2.xml
通常はコマンドはエコーされますが、@をつけるとエコーされません。
del temp.log "del temp.log"と実行コマンドが表示される @del temp.log 実行コマンドは表示されない
"echo off"を実行すると、それ以降のエコーは抑止されます。通常はバッチファイル の先頭に
@echo offを置きます。
echoで改行するにはecho;と書きます。
echo;これで";"がエコーされず改行となる理由は分かりませんが、Windowsバッチ世界 は理由のある世界ではなく、「たまたま世界」なのでこういうもんだ と思ってください。設計思想のないままたまたま出来上がったものが仕様と なっているのです。
標準出力と標準エラーをまとめてリダイレクトするには
というとんでもない構文を用います。"2>&1 kekka.txt"ではありません。コマンド > 出力ファイル 2>&1 例) dir /w > kekka.txt 2>&1
なお、標準出力と標準エラーを別々に標準エラーのみをリダイレクトするなら、
です。コマンド >出力ファイル 2> err出力ファイル
無意味なメッセージの抑止 "del *.log > NUL 2>&1"
Windowsのコマンドは余計なメッセージを出します。
例えばdelコマンドで対象ファイルが存在しない場合などメッセージ
は通常不要です。copyコマンドでもcopyが成功した場合メッセージ
は不要です。
これらの不要なメッセージにより重要なメッセージがマスクされて しまうことは往々にして起ります。
残念ながらコマンド自体には「鬱陶しい邪魔な作業妨害メッセージ抑止」 のオプションはありません。
UNIXであれば/dev/nullにリダイレクトすることによりメッセージを 見せなくできますが、WindowsではNULにリダイレクトします。 Lが一個であることに注意が必要です。NULLでなくNUL
基本的にdelコマンドは標準エラーと標準出力を捨てます。削除
不能のエラーが見えなくなるのはちょっと心配ですが、しかた
ありません。
copyは標準出力のみ捨てます。
なお、システムの設定によってはこの方法をとると"NUL"というファイルが 作成されてしまうことがあるということです。ま、大して害はないので NULというファイルは見て見ぬふりしましょう。 実はNULが作られるというのは間違いでNULL(Lが2つ)と書いて しまったんじゃないかと思っています。普通の人ならNULL と書きます。普通の人が間違うように設計してあるのが Windowsなのです。
そんなの"copy A B C D\ "でいいじゃん。と考えた貴方。貴方は普通で真っ当です。 しかしWindowsバッチ世界は普通でも真っ当でもありません。
コピーコマンドはソースを1個しか指定できないのです。*で複数ファイルを表し ますが、コマンド指定はあくまで1個しかできないのです。
ファイルA,B,CをフォルダDにコピーしたいばあいは次の呪文を唱えます。
for文に関しては次に述べます。なお、このような定型"呪文"以外は動作はcallでサブルーチン とすべきです。set SRC=A B C set DIST=D\ for %%f in ( %SRC% ) do copy %%f %DIST%
フォルダをコピーするxcopyはdelやcopyなどと異なり、組み込みの コマンドではありません。
xcopyの置いてあるフォルダ
C:\WINDOWS\system32にパスが通っていない場合動作しません。 パスはたまに壊れることがあります。
xcopy(または他のあるはずのコマンド)が無いと言われた場合、[コントロールパネル]- [システム]-「詳細設定」-[環境変数]でpathをチェックします。
注意しなくてはならないのが、
xcopyはフォルダコピーコマンドではなく、フォルダの中のファイルコピーコマンドだということです。
これは恐ろしく馬鹿げていて、例えば、フォルダD:\A\CをD:\Bの下にコピーしようと
xcopy D:\A\C D:\B
とすると、何と!D:\B\Cが出来上がるのではなく、Bの下にCのファイル群が展開されて しまうのです。すんげえ仕様だ!
フォルダD:\A\CをD:\Bの下にコピーするには
xcopy /S /Q /I /Y D:\A\C D:\B\C
とする必要があります。バッチ記述で/Iを忘れると、行く先はディレクトリかどうか などと訳の分からないことを言い始めるので必ず/Iが必要です。
/Yを忘れるとオーバーライトするかどうか聞いてきます。間違いのもとですので 必ず付けます。
実際の仕事ではオーバーライトで壊れる壊してしまうことなどありません。 数多く発生するのは一部ファイルがオーバーライトされず、全体が訳の分からない 状態になることです。
ディレクトリの削除はrd /Q /Sで行います。
forによる繰り返しができます。繰り返し実行させる内容は通常サブルーチン に記述します。サブルーチン化するのは変数設定に関する問題を回避するためです。 注意が必要なのはgoto ラベルには:は不要だが、 call :ラベルには:が必要という点です。 ただしgoto :eofは:が必要らしい。
形式:
例:(test.bat)for %%変数名in (A B C) do ( call :サブルーチン名 引数 <-- 単純なものは直接書いても良い ) 繰り返し外の動作 goto END <-- プログラム終了(exitは使わないこと) :サブルーチン名 <-- サブルーチンの入り口 繰り返し動作 goto :eof <-- サブルーチン終了 ::------ :END
@echo off pushd %~dp0 for %%f in (A B C) do ( call :FUNC %%f ) goto END ::---------------------------------- :FUNC echo %1 goto :eof ::---------------------------------- :END result=%ERRORLEVEL% if not "%1"=="" goto NOPAUSE pause :NOPAUSE popd exit /b %result%
実行結果を示します。
A B C 続行するには何かキーを押してください . . .
サブルーチンの終わりは"goto :eof"です。複数のサブルーチンを並べる
ことができます。
pauseを入れてあるのは、バッチファイルをクリックで動かした場合
窓が消えてしまわないようにするためです。
先頭のサブルーチンの前でgoto :eofで終了させるか、最後に:END
ラベルを置きgoto :ENDで飛ばします。
ここの例のような単純なものの場合、サブルーチンを呼ばす 直接コマンドを書くことも可能ですが、定型のもの以外はあまり勧められません。
for %%f in (A B C) do echo %%f
doの括弧内に複数動作を書くこともできますが 変数のスコープにバグ(Windowsの常としてバグのことを 仕様と呼んでいますが)があり、避ける方法は用意されて はいますが、あまりにも汚く見づらく間違いの元なので 絶対にその形式は用いてはなりません。
ifは次の形式です。普通の言語のようにif文と解釈してはなりません。 条件行、else行、終了行の間に動作が入ると考えてください。else行は 省略可能です。
形式:
if 条件 ( <-- 条件行 call :サブルーチン名 引数 <-- 単純なものは直接書いても良い ) else ( <-- else行 call :サブルーチン名 引数 <-- 単純なものは直接書いても良い ) <-- 終了行
注意しなくてはならないのがelseと括弧の間に空白が必要 だということです。
forの動作、ifの括弧の中もサブルーチン呼び出しにしてあります。この例では ifの方は括弧内に書いても全く問題はないのですが、 この習慣をつけておく方が良いと考えました。
例:(rename.bat)
:: ファイル名を変更する @echo off pushd %~dp0 set count=0 for %%f in (*.txt) do ( call :FUNC %%f ) echo %count% files goto END ::---------------------------------------- :FUNC set xx=%1 set xx=%xx:ABC=XYZ% if not %1== %xx% ( call :FUNC2 %1 %xx% ) else ( echo ignore %1 ) goto :eof ::---------------------------------------- :FUNC2 echo %1 to %2 set /a count=%count%+1 goto :eof ::---------------------------------- :END result=%ERRORLEVEL% if not "%1"=="" goto NOPAUSE pause :NOPAUSE popd exit /b %result%
この例はファイル名を一括変更(ABCをXYZに)するものですが、変更の代わりにechoしています。
"echo %1 to %xx%"を"ren %1 %xx%"に変えればファイル名変換になります。
ABCを含まないファイルは無視しています。
AAA.txt,AB.txt,ABC.txt,AbCD.txt,ABCDE.txt,XYZABCZYZ.txtがある 環境で動かした結果を載せませす。
XYZABCXYZ.txt to XYZXYZXYZ.txt ABC.txt to XYZ.txt ignore AAA.txt ignore AB.txt ABCDE.txt to XYZDE.txt AbCD.txt to XYZD.txt 4 files 続行するには何かキーを押してください . . .
SET
setには幾つかオプション指定があります。
- /a:数値演算結果をセットします。
set /a count=%count%+1
ifの条件
未
変数にファイル名が入っている場合、その一部を参照する修飾法があります。
コマンド引数は%の後ろに一桁の数字を入れ、参照できます。
%0はコマンドそのものであり、%1は第一引数です。
%と数字の間に"~オプション文字"をはさむことにより、ファイル名の一部を
取得することができます。
- %~dp1:第一引数のドライブ名とフォルダ名
- %~f2:第二引数のファイルフルパス
実際のバッチファイルで動かした例を載せます。%0はこのバッチファイル そのものです。"%%"は変数参照ではなく単なる%文字となります。"echo;"は改行を出しています。
バッチファイル例:(test2.bat)
:: 変数のファイル名取得修飾テスト @echo off echo %%0 引数そのもの echo %0 echo; echo %%~f0 ファイルフルパス echo %~f0 echo; echo %%~d0 ドライブ文字 echo %~d0 echo; echo %%~p0 フォルダ名 echo %~p0 echo; echo %%~n0 ファイル名(フォルダ、拡張子無し) echo %~n0 echo; echo %%~x0 拡張子 echo %~x0 echo; echo %%~dp0 ドライブ文字とフォルダ名 echo %~dp0 echo; echo %%~dpnx0 ドライブ名+フォルダ名+ファイル名+拡張子 echo %~dpnx0 echo; pause
出力:
%0 引数そのもの "Q:\BLG_ネタ\271Windowsスクリプト\sample1\test2.bat" %~f0 ファイルフルパス Q:\BLG_ネタ\271Windowsスクリプト\sample1\test2.bat %~d0 ドライブ文字 Q: %~p0 フォルダ名 \BLG_ネタ\271Windowsスクリプト\sample1\ %~n0 ファイル名(フォルダ、拡張子無し) test2 %~x0 拡張子 .bat %~dp0 ドライブ文字とフォルダ名 Q:\BLG_ネタ\271Windowsスクリプト\sample1\ %~dpnx0 ドライブ名+フォルダ名+ファイル名+拡張子 Q:\BLG_ネタ\271Windowsスクリプト\sample1\test2.bat 続行するには何かキーを押してください . . .
変数の部分文字列へのアクセス
変数の文字列の一部を置き換えたり、文字位置指定で取り出したりすることができます。(未)
| 固定リンク