Update:2017/04/28-01:39:00
Windows 2000、XPで使えるようになった拡張機能を利用しています。
たいした処理はしていませんが、何かの参考や役に立てば幸いです。
※Windows 9x/meでは利用できません。ご了承ください。
このバッチサブルーチンはそのままでは大して役に立ちませんが、多少手を入れることで作成したいバッチ処理へ柔軟に対応できると思います。
CやJavaなどを使っておられる方には常識でしょうが、テスト実行用の命令を削除して単一のサブルーチンのみでファイルを保存しておけば、必要に応じてCALLコマンドを使って自由に呼び出すことができ便利だと思います。(INCLUDEやMACRO、REQUIRE機能のように使えます。)
また、引数1が省略された場合や「/?」が指定された場合に使い方の説明を表示するようにしておけばほぼ完璧です。
(例)サンプルの文字列操作に含まれている文字列の検索処理をファイル化します。
「文字列操作」から太字の部分を取り出し、赤字の部分を追加します。
編集が終わったら、ファイル名「strpos.bat」で(フォルダー:batlibなどに)保存します。
バッチから呼び出すときは、「call %lib%strpos 対象文字列 検索文字列」のように呼び出します。
「%lib%」には、「strpos.bat」へのパスを設定しておきましょう。(例:set lib=batlib\)
使い方を忘れたときは、コマンドラインから「%lib%strpos /?」で説明を表示します。
文字列操作の基本パターンです。
コマンドプロンプトで使えると便利そうな文字列の長さと文字列の位置検索をサブルーチンにしてみました。
文字列の抽出と置換は環境変数の拡張機能でまかないます。
@title 文字列の操作
@echo off
set str=CommandLine Interface コマンドライン
echo set str=%str%
echo.
echo 文字列の長さ
echo call :strlen %str%
call :strlen "%str%"
echo str=%strlen%
echo.
echo 文字列の検索
echo call :strpos "%str%" "コマンド"
call :strpos "%str%" "コマンド"
echo match=%strpos%
echo.
echo 文字列の抽出
echo str=%str%
echo set substr=%str:~22,4% (%は半角で入力します。)
set substr=%str:~22,4%
echo substr=%substr%
echo.
echo 文字列の置き換え
echo %str:and= & % (%は半角で入力します。)
set str=%str:and= & %
echo str=%str%
echo.
echo on
@exit /b
REM----------------ここからサブルーチン
REM 文字列の長さを求めます。
:strlen
rem echo on
set strlen_string=%~1
set strlen=0
if "%strlen_string%" == "" goto :EOF
:strlen_while
call set ch=%%strlen_string:~%strlen%,1%%
if "%ch%" == "" goto strlen_wend
set /a strlen+=1
goto strlen_while
:strlen_wend
rem @echo off
goto :EOF
REM 文字列の位置を求めます。
:strpos
set source=%~2
set source_len=0
:strpos_source_len_while
call set ch=%%source:~%source_len%,1%%
if "%ch%" == "" goto strpos_source_len_wend
set /a source_len+=1
goto strpos_source_len_while
:strpos_source_len_wend
set target=%~1
set pos=0
call set wk=%%source:~0,1%%
:compare_ch
call set ch=%%target:~%pos%,1%%
if "%ch%" == "" set pos=0&goto compare_ch_end
if not "%ch%" == "%wk%" goto compare_ch_next
call set substring=%%target:~%pos%,%source_len%%%
if "%substring%" == "%source%" goto compare_ch_end
:compare_ch_next
set /a pos+=1
goto compare_ch
:compare_ch_end
set strpos=%pos%
goto :EOF
REM 文字列の位置を求めます。
:init
if "%~2" == "" set strpos=1& goto :EOF
if "%~1" == "" set strpos=0& goto :EOF
if "%~1" == "/?" goto help
:strpos
set source=%~2
set source_len=0
:strpos_source_len_while
call set ch=%%source:~%source_len%,1%%
if "%ch%" == "" goto strpos_source_len_wend
set /a source_len+=1
goto strpos_source_len_while
:strpos_source_len_wend
set target=%~1
set pos=0
call set wk=%%source:~0,1%%
:compare_ch
call set ch=%%target:~%pos%,1%%
if "%ch%" == "" set pos=0&goto compare_ch_end
if not "%ch%" == "%wk%" goto compare_ch_next
call set substring=%%target:~%pos%,%source_len%%%
if "%substring%" == "%source%" goto compare_ch_end
:compare_ch_next
set /a pos+=1
goto compare_ch
:compare_ch_end
set strpos=%pos%
goto :EOF
:help
echo %0の使い方
echo 書式:strpos 対象文字列 検索文字列
echo 戻り値:検索文字列の先頭位置 -> strpos == %strpos%
echo 検索文字が見つからなかったとき -> 0
echo 検索文字が""(空文字)の時 -> 1
echo 対象文字が""(空文字)の時 -> 0 (ただし、検索文字が""の時は 1)
echo 分かると思いますが「strpos /? ? 」は、検索できません。(説明画面が出ます。)
goto :EOF
最近のプログラミングスタイルは、オブジェクト指向によるClassの作成やデータベースを利用したデータオリエンテッドなプログラミングが主流ですが、バッチファイルは頑固なオヤジ風のアルゴリズム中心のプログラミングスタイルがよく似合います。
まあ、言語やプログラミングスタイルに関わらず、プログラムの作成方法はほぼ同じです。
バッチ処理でも、まずは、処理の内容を紙に書いて、必要なデータと処理条件を整理していきます。
システムの開発なんかだと、この段階で評価基準や工数・費用見積もり、テストの仕様、その他プレゼン書類、設計書類を書きまくるわけですね。
バッチ処理では、あまり形式にこだわる必要はないと思いますので(業務用のデータベースを扱うバッチなどは仕様書などがきちんと残ってないと困るかもしれませんが...)ワープロかテキストエディタなんかでざっくりと文書を書いていきます。
で、書いた文書に、より細かい動作やデータの情報を書き加えて、アルゴリズム+データ=プログラミングとします。
作った文書は、そのままドキュメントとして残しておきます。
つぎは、ある時刻から指定時間経過したときの時刻を求めるバッチ処理を作成する例です。
ここでは、ある時刻から指定時間が経過した後の時刻を求めるバッチ処理を作成します。
処理の条件は、
1.時刻は00:00:00から23:59:59の形式の文字列で与え、同様に表示する。
計算結果として日をまたぐことも考慮する。
2.最初のある時刻は、環境変数に入力して与えるものとする。
コマンド引数または標準入力から与えられる。
3.経過時間の指定は、環境変数に「秒数」として与えられるものとする。
コマンド引数または標準入力から与えられる。
4.経過時間の秒数は、整数で負の数を含めて(-2147397248〜2147397247の範囲で)与えられるものとする。
与える経過時間を -2^31(-2147483648) 〜 2^31-1(2147483647)とすると、計算中に符号が変化し正常に計算ができないため、1日分の秒数(86400)を加減しています。
5.経過時間の計算で日をまたぐ場合は、またぐ日数を結果に付加する。
例えば結果の表示を days日hh:mm:ss のようにする。
と考えます。
必要な処理は、次のようになります。
時刻計算
ア.環境変数への時刻の設定
イ.経過時間の設定
ウ.設定時刻と経過時間から計算時刻を求める。
ここに、もう少し細かいデータと処理の内容を加えます。
時刻計算
ア.環境変数への時刻の設定
ア−1.コマンド引数のチェックを行いデータが設定されていない場合は、標準入力からの入力を促す。
ア−2.環境変数に設定された時刻表示が正常な範囲にあるかチェックする。
イ.経過時間の設定
イ−1.コマンド引数のチェックを行い時刻が設定されていない場合は、標準入力からの入力を促す。
イ−2.環境変数に設定された経過時間が正常な範囲にあるかチェックする。
ウ.設定時刻と経過時間から計算時刻を求める。
ウ−1.設定時刻を 00:00:00 からの秒数に変換する。
ウ−2.設定時刻の秒数に経過時間の秒数を加える。
ウ−3.計算結果を時刻表示に変換する。
順次詳細を加えていきます。
時刻計算
ア.環境変数への時刻の設定
ア−1.コマンド引数のチェックを行いデータが設定されていない場合は、標準入力からの入力を促す。
引数1が空であれば、キーボードから環境変数 tm に入力
環境変数 tm が空であれば、現在時刻を環境変数 tm に入力(仕様の変更・追加です。)
でなければ 引数の値を環境変数 tm に設定
ア−2.環境変数に設定された時刻表示が正常な範囲にあるかチェックする。
環境変数 tm が「/?」なら説明画面へ進む
環境変数 tm から先頭2文字を取り出し、hh へコピー
hh の先頭1文字目が 0 であれば、hh の2文字目が 0 でなければ、hh の1文字目を削除する。
hh が -1 より大きいならば、hh が 24 未満であれば、 次へ
でなければ エラー表示へ進む
引数1の3文字目から1文字取り出し、変数 delim に格納する
delim は ":" ならば 次へ でなければ エラー表示へ
環境変数 tm から先頭2文字を取り出し、mm へコピー
mm の先頭1文字目が 0 であれば、mm の2文字目が 0 でなければ、mm の1文字目を削除する。
mm が -1 より大きいならば、mm が 60 未満であれば、 次へ
でなければ エラー表示へ進む
引数1の3文字目から1文字取り出し、変数 delim に格納する
delim は ":" ならば 次へ でなければ エラー表示へ
環境変数 tm から先頭2文字を取り出し、ss へコピー
ss の先頭1文字目が 0 であれば、ss の2文字目が 0 でなければ、ss の1文字目を削除する。
ss が -1 より大きいならば、ss が 60 未満であれば、 次へ
でなければ エラー表示へ進む
イ.経過時間の設定
イ−1.コマンド引数のチェックを行い時刻が設定されていない場合は、標準入力からの入力を促す。
引数2 は "" ならば 変数pastにキーボードから入力させる。
past は "" ならば pastを0に設定(仕様の変更・追加です。)
でなければ 引数2を変数pastにコピー
イ−2.環境変数に設定された経過時間が正常な範囲にあるかチェックする。
past は -2147397248 より大きいならば
past は 2147397247 未満ならば、次へ でなければ エラー表示へ
でなければ エラー表示へ
ウ.設定時刻と経過時間から計算時刻を求める。
ウ−1.設定時刻を 00:00:00 からの秒数に変換する。
a)hh * 3600 + mm * 60 + ss → st
ウ−2.設定時刻の秒数に経過時間の秒数を加える。
a)st + past → st
ウ−3.計算結果を時刻表示に変換する。
a)日数を求めます。
st / 86400 → days
b)日数を除いた秒数を求めます。
st - days * 86400 → st
(0 > st)か?
はい st + 86400 → st
days - 1 → days
次へ進む
c)秒数を hh:mm:ss の形式に変換します。
st / 3600 → ho
st - ho * 3600 → st
st / 60 → mo
st - mo * 60 → st
st → so
ho + 100 → ho
ho の右2桁 → ho
mo + 100 → mo
mo の右2桁 → mo
so + 100 → so
so の右2桁 → so
days日ho:mo:so を tt に格納して表示する。
さらに詳細と修正を加えていきます。
時刻計算
ア.環境変数への時刻の設定
ア−1.コマンド引数のチェックを行いデータが設定されていない場合は、標準入力からの入力を促す。
a)(引数1 == "")か?
はい 環境変数tmにキーボードから入力させる。
(tm == "")か?
はい 現在時刻を環境変数tmにコピー
次に進む
いいえ 引数1を環境変数tmにコピー
次へ進む
ア−2.環境変数に設定された時刻表示が正常な範囲にあるかチェックする。
a)(tm == "/?")か?
はい 使い方の説明へ進む
b)引数1の先頭から2文字取り出し、変数hhに格納する
(hh の先頭1文字が 0)か?
はい (hh の2文字目は 0 でない)か?
はい hh の先頭1文字を削除する。
いいえ 次へ進む
いいえ 次へ進む
(-1 < hh)か?
はい (24 > hh)か?
はい 次へ進む
いいえ エラー表示へ進む
いいえ エラー表示へ進む
c)引数1の3文字目から1文字取り出し、変数delimに格納する
(delim == ":")か?
はい 次へ進む
いいえ エラー表示へ進む
d)引数1の4文字目から2文字取り出し、変数mmに格納する
(mm の先頭1文字が 0)か?
はい (mm の2文字目は 0 でない)か?
はい mm の先頭1文字を削除する。
いいえ 次へ進む
いいえ 次へ進む
(-1 < mm)か?
はい (60 > mm)か?
はい 次へ進む
いいえ エラー表示へ進む
いいえ エラー表示へ進む
e)引数1の6文字目から1文字取り出し、変数delimに格納する
(delim == ":")か?
はい 次へ進む
いいえ エラー表示へ進む
f)引数1の7文字目から2文字取り出し、変数ssに格納する
(ss の先頭1文字が 0)か?
はい (ss の2文字目は 0 でない)か?
はい ss の先頭1文字を削除する。
いいえ 次へ進む
いいえ 次へ進む
(-1 < ss)か?
はい (60 > ss)か?
はい 次へ進む
いいえ エラー表示へ進む
いいえ エラー表示へ進む
イ.経過時間の設定
イ−1.コマンド引数のチェックを行い時刻が設定されていない場合は、標準入力からの入力を促す。
a)(引数2 == "")か?
はい 変数pastにキーボードから入力させる。
(past == "")か?
はい pastを0に設定
次に進む
いいえ 引数2を変数pastにコピー
次へ進む
イ−2.環境変数に設定された経過時間が正常な範囲にあるかチェックする。
a)(-2147397248 < past)か?
はい (2147397247 > past)か?
はい 次へ進む
いいえ エラー表示へ進む
いいえ エラー表示へ進む
ウ.設定時刻と経過時間から計算時刻を求める。
ウ−1.設定時刻を 00:00:00 からの秒数に変換する。
a)hh * 3600 + mm * 60 + ss → st
ウ−2.設定時刻の秒数に経過時間の秒数を加える。
a)st + past → st
ウ−3.計算結果を時刻表示に変換する。
a)日数を求めます。
st / 86400 → days
b)日数を除いた秒数を求めます。
st - days * 86400 → st
(0 > st)か?
はい st + 86400 → st
days - 1 → days
次へ進む
c)st / 3600 → ho
st - ho * 3600 → st
st / 60 → mo
st - mo * 60 → st
st → so
ho + 100 → ho
ho の右2桁 → ho
mo + 100 → mo
mo の右2桁 → mo
so + 100 → so
so の右2桁 → so
days日ho:mo:so を tt に格納して表示する。
エ.使い方の説明を表示する。
オ.エラー表示を行う。
随時、処理の追加や削除、順序の入れ替えなどを加えます。
詳細が十分書き込まれ、コマンドで置き換えられるようになったらバッチ処理は完成です。
ただし、バッチ処理特有の変数展開やリダイレクト/パイプを行う記号の取り扱いに十分注意してください。
FORコマンドや遅延展開、CALLコマンドの活用などを検討しましょう。
バッチの動作確認が終わったら、エラーの発生条件や処理を実行するための条件、不具合の状況など問題点を洗い出し記録しておきましょう。
(修正するかどうかはともかく、後できっと役に立ちます。)
連絡先:お問い合わせフォーム