DOSのコマンドをファイルに書いておき、バッチとして実行することが出来る。(UNIXのシェルスクリプトに相当)
|
|
|
C:\temp>test.bat C:\temp>test C:\>C:\temp\test.bat C:\>"C:\temp\test.bat" …ダブルクォーテーションでくくっても可
C:\temp> test.bat aaa bbb cccところが、引数の区切り文字はスペースだけではない。 等号﹁
=
﹂やセミコロン﹁;
﹂・カンマ﹁,﹂
も区切り文字として認識される。すなわち、等号をデータとして渡すことが出来ない。
ダブルクォーテーションで囲めば渡すことは出来るが、受け取った側で囲み文字を除去する必要がある。
> type test.bat
@echo args: %1 %2 %3 %4
> test.bat a=b,c;d
args: a b c d
> test.bat "a=a" "b,b" "c;c" d
args: "a=a" "b,b" "c;c"d
参考‥ Microsoftのセミコロン、等号 (=)、バッチ ファイルの引数として
@echo offデフォルトでは、バッチファイルの実行時には コマンドを表示してからそのコマンドを実行する︵UNIXのsh -xのようなもの︶。 ﹁echo off﹂を実行しておけば、それ以降はコマンドを表示しなくなる。 しかし﹁echo off﹂だけだと、その文︵echo offそのもの︶だけは表示されてしまう。そこで﹁@﹂を付けて、その文も表示しないようにしている。完璧。 ︵だから、﹁echo off﹂をせずに、全てのコマンドに﹁@﹂を付けてもいいわけだが︶ ︵ちなみに、バッチでなく、コマンドプロンプトから直接﹁echo off﹂をすると面白い事に(爆) 分かると思うけど、戻すには﹁echo on﹂ね︶
echo "start"
test.bat
echo "ここへは戻って来ない!"
制御を戻したい場合には、CALL命令を使う。︵戻り値を返すには、﹁exit /b﹂を使用する︶
echo "start" call test.bat echo "戻ってきた" %ERRORLEVEL% pause﹁exit /b﹂でなく﹁exit﹂で終了するバッチファイルを呼び出すと、exitが呼ばれた時点でコマンドプロンプトが終了してしまう。 この場合はcmd /cを使ってバッチを呼び出してやると、﹁exit 戻り値﹂で設定した戻り値が取得できる。
echo "start" cmd /c test.bat echo "返ってきた♪" %ERRORLEVEL% pause
title ウィンドウのタイトル
set BATDIR=%~dp0%0はバッチファイルそのものを表し、~dはそのドライブ、~pはそのパスを表す。︵~dpでドライブ+パス︶ ﹁C:\temp\test.bat﹂内で上記のコマンドを実行すると、環境変数BATDIRは﹁C:\temp\﹂となる。 末尾に﹁\﹂が付いているので、﹁%BATDIR%\zzz.txt﹂とすると、 実際の展開時は﹁C:\temp\\zzz.txt﹂となり、﹁\﹂が2つ付いてしまう。悪影響があるどうかは、そのパスを受け取るコマンド次第。 ﹁%BATDIR%zzz.txt﹂とすれば問題無いわけだが、一見すると違和感がある。 →UNIXでシェルスクリプトファイルの場所を取得する方法
変数 | 内容 | UNIX相当 |
---|---|---|
%ERRORLEVEL% |
直前に実行されたコマンド(や関数)の戻り値 | $? |
%0 |
実行コマンド名 | $0 |
%1 %2 … %9 |
実行時引数(個別) | $1 $2 … |
%* |
全引数 | $*
$@ |
"abc"
﹂︵abcというファイルは存在しない︶、%2が﹁zzz.
txt
﹂︵存在する︶とする。
指定方法 | 説明 | 例 |
---|---|---|
%~変数 |
%変数 の値がダブルクォーテーションでくくられているとき、ダブルクォーテーションを削除したものになる。 |
echo %~1 →「 abc 」が表示される |
%~f変数 |
%変数 の値をフルパス(絶対パス)に変換したものになる。「 .. 」等の相対パス指定が入っていた場合、それらが取り除かれた正しい状態になる。 |
echo %~f1 →「 C:\temp\abc 」が表示される |
%~d変数 |
%変数 の値をフルパスで扱う際のドライブ名になる。 |
echo %~d1 →「 C: 」が表示される |
%~p変数 |
%変数 の値をフルパスで扱う際のパス名になる。 |
echo %~p1 →「 \temp\ 」が表示される |
%~dp変数 |
%変数 の値をフルパスで扱う際のドライブ+パス名になる。[2009-11-14] |
echo %~dp1 →「 C:\temp\ 」が表示される |
%~n変数 |
%変数 の値のファイル名部分(拡張子なし)になる。 |
echo %~n2 →「 zzz 」が表示される |
%~x変数 |
%変数 の値の拡張子(ピリオド付き)になる。 |
echo %~x2 →「 .txt 」が表示される |
%~nx変数 |
%変数 の値のファイル名(拡張子あり)になる。 |
echo %~nx2 →「 zzz.txt 」が表示される |
%~s変数 |
%変数 の値を短縮形(dir /xと同等)に変換したものになる。[2008-08-02]はずなのだが、なんだかバグがある(正しい形にならない事がある)。(WindowsXP SP3) |
|
%~a変数 |
%変数 の値のファイルが存在するとき、そのファイルの属性になる。 |
echo %~a2 →「 --a------ 」が表示される |
%~t変数 |
%変数 の値のファイルが存在するとき、そのファイルの更新日時になる。 |
echo %~t2 →「 2006/07/07 23:59 」が表示される |
%~z変数 |
%変数 の値のファイルが存在するとき、そのファイルのサイズになる。 |
echo %~z2 →「 5 」が表示される |
%~$環境変数:変数 |
%変数 の値のファイルが 環境変数 (例えばPATHやCLASSPATH)で列挙されているディレクトリに存在するかどうか調べ、存在していればそのファイル(フルパス)になる。「~」と「$」の間には前述の f やd 等の指定を入れることが出来、それぞれの指定に従って変換される。この機能を使うと、UNIXのwhichもどきが作れる。 |
%3が「help.exe 」のとき、echo %~dpnx$PATH:3 →「 C:\WINDOWS\system32\help.exe 」が表示される |
if 条件 コマンド |
if 条件 (コマンド) else コマンド if
条件 (コマンド) else (コマンド) |
if 条件 ( |
== EQU NEQ |
比較 |
LSS LEQ |
大小比較 |
NOT |
否定 | if not "%1" == "" echo 引数があります |
DEFINED |
環境変数が存在するとき、真 | if defined TMP echo 有る |
EXIST |
ファイル(やディレクトリ)が存在するとき、真 ファイル名をダブルクォーテーションでくくることも出来るが、末尾のスペースは無視される模様。 |
if exist zzz.txt echo 在る |
ERRORLEVEL |
%ERRORLEVEL%が値以上 なぜこんな演算子がわざわざあるかについては、注意点を参照。 |
if errorlevel 1 echo エラー |
"1"
﹂は数値ではない︵ダブルクォーテーションは数字じゃない︶ので、﹁1
﹂とは異なる。
また、﹁/i﹂オプションを付けると、大文字小文字を無視して比較する。
if/i %1 equ abc (echo 一致!) else (echo 不一致)
for /L %変数 in (開始,増分,終了)
do コマンド |
for /L %変数 in (開始,増分,終了)
do ( |
>for /L %i in (1,1,10) do echo %i→1〜10が表示される。﹁
(10,1,10)
﹂なら10のみ、﹁(11,
1,10)
﹂なら一回も実行されない。
for %変数 in (複数の値) do コマンド |
@for 〜do@コマンド
>for %i in (aa bb cc) do echo %i >for %i in (aa,bb,cc) do echo %i >for %i in (aa;bb;cc) do echo %i﹁aa﹂﹁bb﹂﹁cc﹂が表示される。 値の中に﹁)﹂が含まれていると当然のことながらそこで列挙の終了とみなされ、本来の﹁)﹂がエラーになるので注意。
>for %i in (aa bb) cc) do @echo %i cc) の使い方が誤っています。 >for %i in (aa bb^)cc) do @echo %i aa bb) cc
>for %i in (%CLASSPATH%) do @echo %i環境変数CLASSPATH︵セミコロン区切りでディレクトリが並んでいる︶の中が1つずつ表示される。
for %%i in (%*) do @echo %%i ←バッチ内で使う例バッチの引数が1つずつ表示される。
>for %i in (C:\*) do @echo %i >for %i in (C:\lib1\*.jar C:\lib2\*.jar) do @echo %iワイルドカードが入っていると、そのディレクトリ︵パスが指定されていない場合はカレントディレクトリ︶のファイル名の一覧になる。︵ファイルのみで、ディレクトリは入らない︶ スペースやセミコロン﹁
;
﹂で︵ワイルドカード入りのパスでも︶複数のパスを区切ることも出来る。[2009-02-20]
>for /d %i in (C:\*) do @echo %i﹁/d﹂オプションを付けると、ディレクトリのみが対象になる。
>for /r %i in (*.txt) do @echo %i﹁/r﹂オプションを付けると、サブディレクトリも再帰的に探索対象となる。
>for /f "オプション" %i in (ファイル名) do @echo %i …ファイルの内容(各行)でループ >for /f "オプション" %i in ("文字列") do @echo %i …文字列を処理 >for /f "オプション" %i in ('コマンド') do @echo %i …コマンドの実行結果(標準出力の各行)でループ指定された対象の行ごとにループする。空行は無視される。 デフォルトでは、変数︵上記の例では%i︶には行の先頭の1語が入る。 オプションはダブルクォーテーションでくくって、以下のものを指定する。︵スペース区切りで複数指定可能︶
skip=行数 | ファイルのその行数分だけスキップする。 |
eol=字 | その文字で始まる行は、コメント行として無視する。>for /f "eol=;" %i in (";コメント") do @echo %i |
tokens=番号 | その番号個目のカラムを変数に入れる。(デフォルトでは1)>for /f "tokens=2" %i in ("a b c") do @echo %i b 番号をカンマ区切りで列挙すると、そのカラムが変数に入れられる。 >for /f "tokens=1,3" %i in ("a b c") do @echo %i %j a c ハイフンでカラムの範囲を指定することも出来る。 >for /f "tokens=2-4" %i in ("a b c d e") do @echo %i %j %k b c d 番号の代わりに「*」を指定すると、行の残りの部分全てになる。 >for /f "tokens=1,*" %i in ("a b c d e") do @echo %i -- %j a -- b c d e >for /f "tokens=*" %i in (test.txt) do @echo %i …行を全部表示 |
delims=文字 | デリミター(区切り文字)を指定する。(デフォルトではスペース)>for /f "delims=,; tokens=1-3" %i in ("abc;def,ghi") do @echo %i %j %k abc def ghi |
usebackq | ファイル名指定の引用符の意味を変える。 デフォルトでは 引用符を何も付けないとファイル名指定だが、空白が入っているファイル名には対応していない。 このオプションを付けることにより、ダブルクォーテーションがファイル名指定になる。(したがって空白が入っているファイル名を指定できるようになる) >for /f "usebackq" %i in ("ファイル名") do @echo %i …ファイルの内容でループ >for /f "usebackq" %i in ('文字列') do @echo %i …文字列を処理 >for /f "usebackq" %i in (`コマンド`) do @echo %i …コマンドの実行結果でループ |
@echo off for /f %%i in (test.txt) do set ZZZ=%%i echo ZZZ=%ZZZ%ファイルの内容が複数行ある場合は、全部読み込んで全ての行に対して同じ処理を行うので、最終行の内容が環境変数に入ることになる。 スペース区切りで区切られている単語の4番目だけ取得したいような場合、トークンを指定する。
@echo off type test2.txt for /f "tokens=4" %%i in (test2.txt) do set ZZZ=%%i echo ZZZ=%ZZZ%
this is a pen ZZZ=penファイルの内容が複数行に亘る場合は、必要な行までスキップして、途中でforを抜けるのがいいだろう。
@echo off type test3.txt for /f "skip=2 tokens=4" %%i in (test3.txt) do ( set ZZZ=%%i goto END_LOOP1 ) :END_LOOP1 echo ZZZ=%ZZZ%
this is a pen this is a apple this is a orange this is a foo ZZZ=orange
@echo off for /f %%i in ('date /t') do set ZZZ=%%i echo date=%ZZZ%
date=2012/07/14forにデリミターとトークンを指定すると、ちょっとした加工を行うことも出来る。
@echo off for /f "delims=/ tokens=1-3" %%i in ('date /t') do ( set ZZZ1=%%i%%j%%k set ZZZ2=%%i-%%j-%%k ) echo date=%ZZZ1% echo date=%ZZZ2%
date=20120714 date=2012-07-14※日付の取得に関しては、環境変数DATEを使う方法もある。どちらが楽かなぁ?^^‥
:ラベル |
goto ラベル |
if%~1 == abc goto match echo 不一致 goto :EOF :match echo 一致
:EOF
というラベルは、暗黙に定義されている。ここへのgotoは﹁バッチファイルの終了へ飛ぶ
﹂、すなわち﹁バッチを終了する﹂ということ。
︵通常のgotoではラベル名に﹁:
﹂コロンは不要だが、:EOF
へのgotoには必要︶
なお、﹁exit /b﹂を使えば同じくバッチを終了する。こちらの方が戻り値を返せるのでいいだろう。
:ラベル |
goto %環境変数% |
MS-DOSにはUNIXのcaseに当たるものは無いが、gotoのラベルは変数を使って動的に変えることができるので、これで代用することが出来なくはない。
set CASE=1 goto caseA_%CASE% …CASEの値に応じて、:caseA_1か:caseA_2へ飛ぶ :caseA_1 echo case1 goto :caseA_end :caseA_2 echo case2 goto :caseA_end :caseA_end
このやり方だと、「その他」に当たるものが出来ないが…。
(存在しないラベルへ飛ぼうとするとエラーになるから)
定義 | 呼び出し |
---|---|
:ラベル |
call :ラベル |
@echo off echo 呼び出し前:%* call :sub aa %* bb echo 戻り値:%ERRORLEVEL% echo 呼び出し後:%* exit /b :sub echo 呼び出された:%* exit /b99
>test.bat foo zzz
呼び出し前:foo zzz
呼び出された:aa foo zzz bb
戻り値:99呼び出し後:foo zzz
>test.bat || echo failtest.batが文法的におかしくて失敗した場合にメッセージを出したいならこれでいいが、 test.batの中で﹁exit /b 1﹂のように0以外の値を返したことによってメッセージを出したいなら、これではダメ。 後者の扱いをしたいのであれば、以下の様にすべき。
>test.bat &iferrorlevel 1 echo fail …test.batの戻り値が1以上のとき「echo fail」を実行
>test.bat & if %ERRORLEVEL% neq 0 echo fail
>test.bat & echo %ERRORLEVEL%なぜなら、環境変数はその行全体の実行開始前に展開されるから。 つまり実行前に%ERRORLEVEL%の値が0だったとすると、以下のように展開されてから実行されることになる。
>test.bat & if 0 neq 0 echo fail
>test.bat & echo 0したがって、test.batがどんな値を返して︵%ERRORLEVEL%にセットされて︶も、実行前の値で﹁&﹂以降が処理されるということになる。 ︵この為に、エラーコードチェックに関してはERRORLEVELという演算子がわざわざ用意されているわけだ。これなら値の取得自体はif文の実行時に行うわけだから。︶
>set var=aaa >set var=bbb & echo %var% →「set var=bbb & echo aaa」と展開されてから実行される aaa >echo %var% bbb →「set var=bbb」が実行されなかったわけではないので、後から見ればちゃんとセットされている
set var=1 if %var% == 1 ( →「if 1 == 1 ( set/a var=%var% + 2 set/a var=1 + 2 echo %var% echo 1 ) )」と展開されてから実行される
set list= for %%i in (aa bb) do @set list=%list%;%%i →「for %i in (aa bb) do @set list=;%i」と展開されてから処理されるので、 echo %list% 「@set list=;aa」「@set list=;bb」が実行される
>set var=123 >set var=234 & echo 通常:%var% 遅延:!var! 通常:123 遅延:!var! >cmd /v:on …遅延環境変数を有効にして新しいコマンドプロンプトを起動 >set var=123 >set var=234 & echo 通常:%var% 遅延:!var! 通常:123 遅延:234 >exit …元のコマンドプロンプトへ戻る
set var=1 if %var% == 1 (call :add %var%) exit/b :add set/a var=%var% + 2 echo %var%
set list= for %%i in (aa bb) do @call :add %%i echo %list% exit/b :add @set list=%list%;%1
@echo off more実行例‥
> type aaa.txt | test_more a1 a2 > test_more < aaa.txt a1 a2
@echo off for /f %%i in ('findstr .*') do echo %%i実行例‥
> type aaa.txt | test_echo a1 a2 > test_echo < aaa.txt …なぜか何も出力されないパイプを使ったリダイレクションはちゃんと動くのに、ファイルからの入力は何故か何も出力されない…。 とりあえず、バッチ内部でパイプを経由してやると上手くいくっぽい。 test_echo2.bat‥
@echo off findstr .* | for /f %%i in ('findstr .*') do @echo %%i実行例‥
> type aaa.txt | test_echo2 a1 a2 > test_echo2 < aaa.txt a1 a2ただ このやり方だと、doの後ろでcallを使ってラベルに飛ぼうとすると﹁バッチ スクリプト外でバッチ ラベルを呼び出すことはできません。﹂という実行時エラーになる。 どうやら、パイプの後ろは別バッチのような扱いになるらしく、したがってラベルも別空間になるので認識できないっぽい。 なかなかベストな解決方法が見つからない…(嘆)