cloned.log

2009-02-05 (Thu) VBScript(WSH)の基礎とファイル読み書きや正規表現を使う このエントリーを含むブックマーク このエントリーのブックマークコメント

UNIX系のシェルスクリプトでは簡単にできることでも、Windowsのバッチファイルではできないことが非常に多い。そこで、Windowsの場合はWSHを利用することになる。かなり基本的すぎるところから、よくやりそうなテキスト処理などの例をメモがてら書いてみる。

VBScript

VBScriptはMicrosoft Visual Basic Scripting Editionの略で、Windowsで動くスクリプト言語。WSHはWindows Script Hostの略で、Windows上でスクリプト言語を動作させる実行環境。VBScriptの他にJScriptも動作する。サーバサイドの場合はASP(Active Server Pages)上でVBScriptを動作させることができる。

実行方法とHello World

.vbsでファイルを保存。ダブルクリックで実行できる。Hello Worldは次の通り。

WScript.Echo "Hello World"

GUIからダブルクリックして実行した場合には.vbsがWScript.exeが関連付けされているため、ダイアログにHello Worldと表示される。コマンドプロンプトから実行する場合にはCScript.exeを利用する。コマンドは次のようになる。

C:\Path\To>CScript test.vbs
Microsoft (R) Windows Script Host Version 5.7
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

Hello World

WScript.Echoを利用しない方法としてMsgBox関数を使うこともできる。ただし、MsgBoxを利用するとコマンドプロンプトから実行してもGUIのダイアログが起動するので注意。

MsgBox "Hello World"

変数名やプロシージャ名、ステートメントなど大文字小文字の区別がないため、MsgBoxはMSGBOXなどと書くことができるが、慣例的にステートメントは大文字始まりのアッパーキャメルケースで書かれることが多い。

コメントアウト

行の先頭に'(シングルクォート)を付ける。

'WScript.Echo "Hello World"

複数行コメントはサポートされていない。

変数宣言

Dimを使って変数宣言を行う。JavaScriptのvarと同義だと思われる。Dimによって宣言された変数はスコープの外から利用することができない(Subプロシージャについては後述)。

Sub Hoge()
    Dim str
    str = "inner"
    WScript.Echo str
End Sub

Dim str
str = "outer"

WScript.Echo str
Hoge
WScript.Echo str

出力結果は「outer」「inner」「outer」となる。次のようにSubプロシージャ内のDim宣言を省略すると、外部スコープのDimが利用されるため、変数が上書きされる。

Sub Hoge()
'    Dim str
    str = "inner"
    WScript.Echo str
End Sub

Dim str
str = "outer"

WScript.Echo str
Hoge
WScript.Echo str

よって、この場合の出力結果は「outer」「inner」「inner」となる。

Dimによる変数宣言を省略しても暗黙に宣言されるためエラーにならない。変数宣言のない変数の利用をエラーにしたい場合は「Option Explicit」を利用する。次のコードはエラーとなる。

Option Explicit

x = 0

詳しくはDim ステートメントを参照のこと。

文字列結合

&を使って結合する。

WScript.Echo "Hello" & " " & "World"

改行を使う場合にはvbCrLf(CRLF)やvbCr(CR)、vbLf(LF)が利用できる。

WScript.Echo "Hello" & vbCrLf & "World"

これらの定数はConstantsクラスに属していてどこからでも参照することができる。

詳しくはConstants フィールドを参照のこと。

配列

配列は宣言時に()を付けて宣言する。

Dim ary(2)
ary(0) = "A"
ary(1) = "B"
WScript.Echo ary(0)
WScript.Echo ary(1)

出力結果は「A B」と表示される。Array関数を使って代入することもできる。次は前述と同様の挙動となるが、宣言時に()を付けて宣言することはできない。

Dim ary
ary = Array("A", "B")
WScript.Echo ary(0)
WScript.Echo ary(1)

オブジェクトの代入

通常の文字列や数値は変数に=演算子で代入することができるが、オブジェクトへの参照を代入する場合にはSetステートメントを利用する必要がある。オブジェクトのコピーではなくオブジェクトの参照が代入される。

Dim re
Set re = New RegExp

詳しくはSet ステートメントを参照のこと。

If文

If文は次の通り。

If condition Then

[statements]

[ElseIf condition-n Then

[elseifstatements . . .

[Else

[elsestatements

End If

If...Then...Else ステートメント

比較するときの演算子が「==」ではなく「=」であることに注意。「If hoge = "" Then」のようになる。また、否定の場合はIf Notを利用するので「If Not hoge = "" Then」のようになる。

繰り返し文

回数を指定して繰り返す場合はFor...Nextを使う。

Dim i
For i = 1 To 10
    WScript.Echo i
Next

出力結果は「1」〜「10」となる。

配列などのコレクションを繰り返す場合はFor Each...Nextが便利。

Dim strings(3), str
strings(0) = "A"
strings(1) = "B"

For Each str In strings
    WScript.Echo str
Next

出力結果は「A」「B」となる。

特定の条件で終了させる場合にはDo...Loopを使う。

Dim i
i = 0
Do Until i = 3
    WScript.Echo i
    i = i + 1
Loop

出力結果は「0」「1」「2」となる。

関数

値を返さないプロシージャはSub、値を返すプロシージャはFunctionとして定義する。

Subは値を返さないけれど、引数を参照で渡すことができる(デフォルトでは参照渡し)。値渡しにする場合はByValを付ける。明示的に参照渡しを指定する場合はByRefを付ける。

Sub Hoge(a, ByVal b, ByRef c)
    WScript.Echo a
    WScript.Echo b
    WScript.Echo c
End Sub
Hoge "A", "B", "C"

出力結果は「A」「B」「C」となる。呼び出すときに「Hoge("A", "B", "C")」というように括弧を使うとエラーになるので注意。

Functionプロシージャも引数はSubプロシージャと同様にデフォルトは値渡しで、変更する場合はByValを付ける。

Function Hoge(a, ByVal b, ByRef c)
    WScript.Echo a
    WScript.Echo b
    WScript.Echo c
    Hoge = "D"
End Function
WScript.Echo Hoge("A", "B", "C")

出力結果は「A」「B」「C」「D」となる。戻り値はreturnを使わずにFunctionプロシージャ名に代入する。呼び出しはSubプロシージャと異なり括弧を使う。

ややこしいのが、戻り値を受け取らないFunctionプロシージャはSubプロシージャとみなされる模様。

Function Hoge(a, ByVal b, ByRef c)
    WScript.Echo a
    WScript.Echo b
    WScript.Echo c
    Hoge = "D"
End Function
Hoge("A", "B", "C")

上記は戻り値を受け取っていないのでSubプロシージャとみなされ、括弧を付けて呼び出しているのでエラーになる。ただ、完全にSubプロシージャなわけでもなくてFunctionをSubに置き換えると「Hoge = "D"」の行でエラーになる(Subプロシージャは戻り値を持つことができない)。

アクセス修飾子を使うこともできるけれどここでは省略。

詳しくは、Sub ステートメントFunction ステートメントを参照のこと。

日付を利用する

日付を表すオブジェクトがいくつか存在する。

Dim d, n, t
d = Date()
n = Now()
t = Time()
WScript.Echo d
WScript.Echo n
WScript.Echo t

出力結果は上から「yyyy/MM/dd」「yyyy/MM/dd hh:mm:ss」「hh:mm:ss」の形式になる。一つ一つを分解して取得する場合はそれぞれに対応した関数を使用する。

Dim d, t
d = Date()
t = Time()
WScript.Echo Year(d)
WScript.Echo Month(d)
WScript.Echo Day(d)
WScript.Echo Hour(t)
WScript.Echo Minute(t)
WScript.Echo Second(t)

出力結果は上から「年」「月」「日」「時」「分」「秒」となる。

日付の計算にはDateAddなどを利用する。詳しくはDateAdd 関数を参照のこと。

ディレクトリのファイル一覧を取得

ファイルを操作するにはCreateObjectでScripting.FileSystemObjectを呼び出して利用する。オブジェクトなのでSetステートメントで代入する。ディレクトリのファイル取得はgetFolderを使ってフォルダオブジェクトを呼び出してからFilesを使って取得する。

Dim fso, dir, file
Set fso = CreateObject("Scripting.FileSystemObject")
Set dir = fso.getFolder(".")
For Each file In dir.Files
    WScript.Echo file.Name
Next

Scripting.FileSystemObjectは実際のファイルリソースではないようなのでClose処理がない。ただし、ファイルストリームを開いた場合などではClose処理にてフラッシュされる。

単一のファイルを取得する場合はGetFileを使う。取得できるのはオブジェクトで、代表的なプロパティの参照は次のようになる。

Option Explicit
Dim fso, file
Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.GetFile("test.vbs")
WScript.Echo file.Name             ' ファイル名
WScript.Echo file.Drive            ' ドライブ名
WScript.Echo file.ParentFolder     ' 親フォルダ
WScript.Echo file.Path             ' パス
WScript.Echo file.Size             ' サイズ
WScript.Echo file.Type             ' 種類
WScript.Echo file.Attributes       ' 属性
WScript.Echo file.DateCreated      ' 作成日
WScript.Echo file.DateLastAccessed ' 最終アクセス日
WScript.Echo file.DateLastModified ' 最終更新日

ファイルを読み込む

OpenTextFileを使って読み込む。第一引数にファイル名、第二引数にモード(1=読み取り専用、2=書き込み専用、3=追記)、第三引数にファイルが存在する場合に新規作成するかどうか(作成する場合はTrue)。

Dim fso, reader
Set fso = CreateObject("Scripting.FileSystemObject")
Set reader = fso.OpenTextFile("test.vbs", 1, False)
Do Until reader.AtEndOfStream
    WScript.Echo reader.ReadLine
Loop
reader.Close()

出力結果は一行ずつ読み込まれた値になる。

ファイルを書き込む

ファイルの読み込みとほぼ同じで、引数を変更するのと、WriteLineで一行書き込むことができる。

Dim fso, writer, i
Set fso = CreateObject("Scripting.FileSystemObject")
Set writer = fso.OpenTextFile("write.txt", 2, True)
For i = 1 To 10
    writer.WriteLine i
Next
writer.Close()

出力されたファイルの内容は改行区切りで「1」〜「10」となる。

追記にする場合は「fso.OpenTextFile("write.txt", 8, True)」というようにOpenTextFileの第二引数を8に変更する。

文字列を区切り文字で配列にする

Splitを使う。

Dim strings, str
strings = Split("A,B,C", ",")
For Each str In strings
    WScript.Echo str
Next

出力結果は「A」「B」「C」となる。

正規表現を使ってパターンに一致するか調べる

Newを使ってRegExpオブジェクトを取得する。パターンはRegExpオブジェクトのPatternプロパティに代入する。

Dim re
Set re = New RegExp
re.Pattern = "^A.*$"
If re.Test("ABC") Then
    WScript.Echo "matched"
End If

大文字小文字を無視する場合はIgnoreCaseにTrueを代入する。

re.IgnoreCase = True

最初の一致のみではなく、文字列全体を検索対象にする場合にはGlobalにTrueを代入する。

re.Global = True

正規表現を使って一致する文字列を取得する

RegExpオブジェクトのExecuteを使う。

Dim re, matches
Set re = New RegExp
re.Pattern = "^A.*$"
Set matches = re.Execute("ABC")
If matches.Count > 0 Then
    WScript.Echo matches(0)
End If

出力結果は「ABC」となる。

正規表現を使ってキャプチャした文字列を取得する

Itemプロパティを利用してSubMatchesを取得する。

Dim re, matches
Set re = New RegExp
re.Pattern = "^([A-F]+)_([A-F]+).+"
Set matches = re.Execute("ABC_DEF_GHI")
If matches.Count > 0 Then
    WScript.Echo matches.Item(0).SubMatches.Item(0)
    WScript.Echo matches.Item(0).SubMatches.Item(1)
End If

出力結果は「ABC」「DEF」となる。

正規表現を使って文字列を置換する

RegExpオブジェクトのReplaceを使う。

Dim re, matches
Set re = New RegExp
re.Pattern = "^ABC_"
WScript.Echo re.Replace("ABC_DEF_GHI", "XXX_")

出力結果は「XXX_DEF_GHI」となる。

正規表現を使って文字列を置換する(後方参照)

RegExpオブジェクトのReplaceを使って置換文字列中に$数字の形式で参照する。

Dim re, matches
Set re = New RegExp
re.Pattern = "^([A-Z]{2})(.*)"
WScript.Echo re.Replace("ABCDE", "XX$2")

出力結果は「XXCDE」となる。

まとめ

Windowsでもそれなりにちゃんとシェルスクリプトみたいなものを簡単に作ることができる。バッチファイルで無理やり頑張らずにVBScriptやJScriptの利用を検討すると良い。

関連: Windowsバッチのかゆいところメモ