リスト1●grepを実行するプログラムのソースコード(VBScript)
リスト1●grepを実行するプログラムのソースコード(VBScript)
[画像のクリックで拡大表示]
図1●今回のサンプルを実行したところ
図1●今回のサンプルを実行したところ
[画像のクリックで拡大表示]

連載目次へ

レシピ
■ WSH(Windows Script Host)5.6以降。(バージョンアップ可能

 UNIXの文字列検索コマンドだったgrepは,今や文字列検索機能プログラムの代名詞となりました。今回はこのgrepを作成してみます。目的のファイルから文字列を抽出して検索する処理を応用すれば,様々な用途がありますよ!

 今回作成するgrepのプログラムは任意の拡張子のファイルを対象に全文検索を行い,指定された語句を含む場合には,その語句が含まれるファイル名と行数を表示します。WSHのHTMLアプリケーション(HTA)として作成します。プログラミング言語はVBScriptです。

単純機能にするため
制限事項がいくつか

 今回のサンプルは,grepプログラムの基礎の基礎となるサンプルなので,多少制限を設けました。

指定したフォルダ(ディレクトリ)のみ検索し,その下の階層は検索しない
指定されたディレクトリにさらにディレクトリが含まれている場合,本当は最下層のディレクトリまで検索したいのですが,これには再帰処理が必要になります。再帰処理を実装するとコードが長くなってしまうので今回はサポートしないことにしました。
検索語句の途中に改行やスペースが入っているとヒットしない
サンプルは“完全一致”のみを対象とするので,対象となる指定語句の途中で改行が入っていると一致しません。この制限を回避するには正規表現を使った検索が必要になります。
シフトJIS形式のテキスト・ファイルのみを対象とする
VBScriptは半角英数文字は読み取れますが,EUCのようなシフトJIS以外の文字コードで書かれた2バイト文字(全角文字)を判読できません。

 これらの制限は皆さんが今回のサンプル・プログラムを改造していく際のポイントでもあります。余力のある人はどんどん機能を拡張してみてください。検索結果を「file://」のリンクにしてクリックすると開ける…という拡張もいいでしょう。

検索のポイントは
InStrRev関数

 プログラム・コードはリスト1[拡大表示]の通りです。grep.htaなどと拡張子.htaのファイルで保存し,ダブルクリックすると起動します。

<html><head><title>htaでgrep</title>
<script language="VBScript">
Sub grep()
    Set WS = CreateObject("WScript.Shell")
    On Error Resume Next 'エラー停止回避
    WS.CurrentDirectory = form1.dir.value
    If Err.Number = -2147024894 Then 'ディレクトリがないというエラー
        MsgBox "指定されたディレクトリは存在しません"
        Exit Sub
    ElseIf Err.Number <> 0 Then
        MsgBox ("エラー番号" & CStr(Err.Number) & " " & Err.Description)
    End If
    Err.Clear ' エラーのクリア
    Set FS = CreateObject("Scripting.FileSystemObject")
    Set TMP = FS.GetFolder(form1.dir.value)
    WS.CurrentDirectory = Lcase(WS.CurrentDirectory)
    X = 0 '連番用の番号
    form1.result.value = "" 'textareaを初期化
    For Each FileName In TMP.Files
        FileName = LCase(FileName) '全部小文字にして
        Y = InStrRev(FileName,".txt")
        If Y>0 Then
            SET FSO = CreateObject("Scripting.FileSystemObject")
            SET FP = FSO.OpenTextFile(FileName,1,FALSE)
            i = 1 '行数カウント用変数(初期値1)
            X = X + 1 'ファイル見つけたぞフラグ
            Do Until FP.AtEndOfStream
                TGT = FP.ReadLine 'ファイル内容1行読み込み
                If TGT<>vbNull Then
                    Z = InStrRev(TGT,form1.words.value)
                End If
                If Z>0 Then
                    form1.result.value = form1.result.value & vbcrlf & FileName & " " & i & "行目"
                End If
                i = i + 1 '行数カウントの追加
            Loop
            FP.Close 'ファイルを閉じます
            SET FP = Nothing 'ファイル・オブジェクトを解放
            SET FSO= Nothing 'FileSystemObjectを解放
        End If
    Next
    SET TMP = Nothing
    SET FS = Nothing
End Sub
</script></head>
<body><form name="form1">
<p>検索対象は指定されたディレクトリの.txt拡張子のファイルになります。</p>
<p>ディレクトリの指定(例: C:\foo\bar)<br />
<input type="text" size="50" name="dir"></p><br />
<p>検索語句<br />
<input type="text" name="words"></p><br />
<p><input type="button" value="grepの実行" onClick="grep()"></p>
<p><textarea name="result" cols="50" rows="20"></textarea></p>
</form></body></html>

 このプログラムは,まず指定したディレクトリの中に,.txtの拡張子を持つファイル(いわゆるテキスト・ファイル)が存在するかどうかをチェックします。ここで使うのがInStrRev関数です。InStrRev関数は,文字列の中に指定された検索語句が入っているかどうかを検索する関数です。語句が含まれれば0以外の数字を返します。テキスト・ファイルが存在した場合,そのファイルを1行ずつ頭から終端まで読み込んで指定された検索語句があるかないかを確認します。検索語句があったら,そのファイルの名前と現在の行数をHTMLフォーム上の<textarea>タグの中に書き出します。ここでも,InStrRev関数を使います。一つのファイルの中に複数の合致部分が見つかった場合にもちゃんと表示するようにしました(図1[拡大表示])。

 「.txtだったら(正確には.txtという文字列がファイル名に存在したらといういい加減な探し方になってます)」という部分はコード内でベタ書きしているので,ここを「.c」や「.php」に変更すれば,テキスト・ファイル以外もgrepできるようになります。ただし.EXE,.DLL,.XLSなどの検索は避けてください。バイナリ・ファイルを読み込んだからといってファイルを破壊することはありませんが,検索結果としてはアテにならないからです。

 grepは,秀丸エディタなどにも機能として搭載されています。複数ソースで構成されたプログラム,HTML系スクリプトなどで,全体的に変更を加えなくてはならないような場合,大変重宝する検索方法です。今回が初見となった皆さんはぜひ積極的に使ってみてください。