VB Script とエラーハンドリング
ちょっと訳ありで VB Script でバッチ処理を書くことになり、いろいろ試行錯誤しました。 もう5年も前に一度扱ったことがあるのですが、正直もう VB 系は覚えていない…。
VB Script というのは、Windows で標準的に使えるスクリプト言語です。 IE の上でも使えますが、Windows に実装されている、WSH (Windows Scriptiog Host) 上でも使えます。 とどのつまり、昔からある .bat ファイルの強化版のようなイメージ。 ためしに、Windows 上で拡張子 .vbs の空ファイルをつくるとアイコンが変わり、そのままダブルクリックでスクリプトファイルが実行できるのが分かります。 🙂
@IT:運用 Windows管理者のためのWindows Script Host入門 第1回 WSHの内部構造 1.Windows管理にWSHを活用しよう
このバッチに代わるものとして、Windows環境ではWindows Script Host(以下WSH)と呼ばれる機能が提供されている。これはWindowsが標準でサポートするスクリプト環境で、テキスト・ベースのスクリプトを記述することで、GUIに頼ることなく、Windows環境でさまざまな処理を実行できる。
これつかうと、簡単に exe を起動したり、ものによってはアプリを自動コントロールできたり、結構個人で使うぶんには便利です。 Skype なんかも COM 対応なので WSH から動かせるみたいですね。 ムードメッセージ変更したりなんかが、UI使わなくてもできるようです。
さて、”バッチ処理” というのは(Windows のバッチではなく一般的な意味)、一括処理系の総称で、反対の言葉はリアルタイム処理となるでしょう。 たとえば、日中帯リアルタイムで入力されたデータを、システム終了後 “バッチ処理” で集計する、といった使い方になります。 1日の売り上げの集計(締め処理)などがこれにあたります。
でもって、VB Script でバッチ処理を書こう、というのが今回の課題になっていました。 VB Script が指定されるというくらいなので、それほど難しいバッチではないのですが、一応業務システム。 落とすわけにはいきません。
とりあえずちょこちょこ書きはじめてみたのですが、ひとつの壁に当たりました。
プログラムにエラーはつきものですが、そのエラーには大きく2つあります。 正常系異常と異常系異常。 なんのことやらですが、文字通りで前者は、動き的にエラーが想定されていて、それが分かった場合にはしかるべき処理をするというのが正常系異常。 異常系異常は、単純にプログラムのバグによっておちることです。 後者は当然リリース時には修正されるものです。
ここでの問題は、正常系異常。 これはスクリプト言語系全般にいえるのですが、組み込み関数などが簡単に異常で落ちます。 まぁ落ちるのはいいとして、それを検知(エラーハンドリング)するのがあまりスマートなほうほうがとれない。
もちろん、プログラム続行不能な場合は落ちるしかないのですが、業務系のシステムでは落ちたことを適切に(正常に)ログに出力するなどして通知したり、そこまでの処理データを処理前にロールバックする必要があります。 これが正常系異常処理です。
以下、かなり前後を省略していますが VB Script で”ファイルを消す”処理です。(そらで書いているので間違っているかも。 イメージだけ)
objFS.Delete("hoge.txt")
こうやると、hoge.txt が消えてくれます。 hoge.txt が存在している場合は正常系。 じゃー、hoge,txt が存在しないこともあるとするとどうなるのかというと、このコードはここの部分でエラーで”落ち”ます。 スクリプトが停止してしまうので、失敗したことの通知すらできません。 なんたってバッチです、画面なんかでません。(画面にでただけでもだめだけど…) これじゃー困っちゃうのです。 🙂
というわけで、ちょっと小細工します。
If onjFS.FileExists("hoge.txt") Then
objFS.Delete("hoge.txt")
Else
log "hoge,txt が消せませんでした。"
WScript.Quit 1
End If
ってなかんじで、ファイルが存在しているときだけ消すようにすると、まぁファイルなくてもおちなくなります。
でもね、気になりませんか? 存在確認をした直後、瞬間に、ファイルが消える可能性があります。 いや、100% に近い確率でありえませんが、それでも可能性はあります。 WP で起きても笑うだけですが、たとえば、これが原発とか病院のシステムだったらと思うと夜も眠れません。(←そんなもんつくったら寝るな!
というのは冗談ですが、ファイルが読み取り属性だったり、もちろん直後に消えることも少しは考えられますし、Delete がおちる可能性はまだまだあります。
Java や C# などのいわゆる高級言語では例外処理という考え方があり、なにかが落ちたらこの処理をはしらせろといった記述ができます。 また、それぞれのメソッドで何が起きる可能性があるかということも言語仕様として非常に明確です。
ちなみに、PHP も 5 から例外処理が構文的には書けるようになっていますが、組み込みの関数はごく一部しか例外を throw してくれないので、組み込み関数の場合はその戻り値をみて自前で例外をなげるしかないようです。 とりあえず PHP の場合あまり処理ごと落ちる関数がないので、これである程度なんとかはなりそうです。
というわけで、VB Script。 くさっても Visual Basic ゆずりということで例外処理くらいかけるだろーと思ってしらべたらでてきました。
On Error Resume Next
On Error Goto 0
…この Resume Next 構文より下はエラーが”なかった”ことになるらしい。 で、Err オブジェクトでエラー番号調べて処理しろと。。 Goto 0 はその解除ですか。 orz
On Error Resume Next
If onjFS.FileExists("hoge.txt") Then
objFS.Delete("hoge.txt")
Else
log "hoge,txt が存在しませんでした。"
WScript.Quit 1
End If
If Err.Number <> 0 Then
log "hoge,txt がなぜか消せませんでした。" & Err.Number
WScript.Quit 1
End If
On Error Goto 0
そりゃないぜ、って感じですが VB だと使えた On Error Goto ラベル ってのが使えないのでこうするしかないようです。
であと、みため美しくないのもあるのですが、エラーを全部無かったことにしてくれるので、異常系異常すら素通ししてしまいます。 つまりスペルミスなどの構文エラーなどもなかったことになるのです。 これがコーディング中には非常に強敵。 そんなわけで、
If SCRIPT_DEBUG = False Then
On Error Resume Next
End If
なんてしておくと便利かと思いました。
今回は処理も簡単な 1000 step 程度のもので、複数人でひとつつくっているわけでもなかったので、なんとなかりましたが、やはりあまり大規模なものには向いていないという感想を持ちました。
とりあえず本気(?)で使う場合は、よく落ちるメソッドがたくさんありますので、正常系だけでなく異常系もよくよく試験してから運用に回すことをお勧めします。 😛