Tech-Ezo (Hokkaido PC and Network Users Group)
 Top Page  |  What's Tech-Ezo  |  Next Seminar  |  Seminar Log  |  Seminar Plan  |  Tips  |  life  |  Link  |
Scr.003 ステップアップ WSH - 使えそうなスクリプトを作って見よう
前ページへ   [ステップアップ WSHへ戻る]   次ページへ


■  Level.10  再帰的にフォルダをたどれるようにする
■ ■

 Level.9までに作成したスクリプトでは、1階層ながらサブフォルダ内にあるテキストファイルを検索対象と出来るようになりました。Level.10ではサブフォルダを再帰的に辿って指定のフォルダ以下全てのサブフォルダを処理対象にします。

 ところで、いきなりこのベージにやってきた人のために、一言添えておきますと、「再帰処理って何だろ?」という話は、Level.9の最初の方で軽く触れていますので、一つ前のページに戻って見て下さい。

スクリプトの構造

 このLevel.10では、サブフォルダの中のサブフォルダの中のサブフォルダ...というように、指定のフォルダ以下全てのサブフォルダを辿っていくのですが、Level.9のスクリプトをどのように修正すれば、このような階層構造を辿って行けるのでしょうか?

 ここで、再度Level.9の構造を見てみましょう。

level9.vbsの構造
(1) テキストファイル処理関数の定義
(2) フォルダ名、検索文字の入力処理
(3) テキストファイル処理関数の呼び出し
(4) サブフォルダ処理
  → サブフォルダの処理のなかでテキストファイル処理関数の呼び出し

 ポイントはサブフォルダを処理する(4)にありそうです。例えば、(4)のサブフォルダ処理の中では、指定のフォルダに格納されているサブフォルダ分、(1)のテキストファイル処理関数を呼ぶわけですが、ここで(1)を呼ぶ時に使ったフォルダ名で、自分自身(サブフォルダ)処理を呼んで見たらどうでしょうか?。この時、自分自身を呼ぶためにサブフォルダ処理を関数化する必要もありそうです。

サブフォルダ処理の内容
001
002
003
004
005
006
007
' 指定のフォルダに格納された各サブフォルダを処理する
Sub FolderSrh(fl)
    For Each FolderName In src.Subfolders
        FileSrh(FolderName)
        FolderSrh(fl)
    Next
End Sub

 上記の状態ですと、srcを定義していないので不完全ではありますが、何となく出来そうな気がしてきました。でも、改めて見てみると、やっていることは、テキストファイル処理を呼んでいるだけで、何か釈然としないものを感じます(え?私だけ?)。

 そこで、どうせテキストファイル処理の後で、サブフォルダをさらに処理するのだから...ということで、テキストファイル処理の最後に、サブフォルダ処理をくっつけて見ました。

テキストファイル処理関数の中にサブフォルダ処理を入れてみた
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
' --< テキストファイル処理関数 >-------------------------------------------------
' 指定のフォルダに格納された各ファイルを処理する
Sub FileSrh(fn)
    Set subf = fso.GetFolder(fn)    ' 引数に指定されたフォルダの情報を取得
    For Each FileName In subf.Files
        FileEx = fso.GetExtensionName(FileName)   ' ファイル名から拡張子を抜き出す
        If LCase(FileEx) = "txt" Then             ' 拡張子を小文字化してから比較
    
          ' テキストファイルの内容読み込み処理
            Set TextFile = fso.OpenTextFile(FileName) ' テキストファイルのオープン
            srhLine = FileName & ":" & vbNewLine      ' 検索結果格納用変数の初期化
            srhFlg = 0                                ' 検索ヒットフラグのクリア
            Do Until TextFile.AtEndOfStream
                tmpLine = TextFile.ReadLine           ' 1行読み込み
    
                If regEx.Test(tmpLine) Then           ' 指定の文字列を含むか?
                    srhLine = srhLine & TextFile.Line - 1 & ":" & tmpLine & vbNewLine
                                                      ' 読み込んだ行のマージ
                    srhFlg =1                         ' 検索がヒットしたので1にする
                End If
            Loop
    
            If srhFlg = 1 Then                        ' 指定の文字列が含まれてたら
                WScript.Echo srhLine
            End If
    
        End If
    Next

    ' 指定のフォルダに格納された各サブフォルダを処理する
    For Each FolderName In subf.Subfolders
        FileSrh(FolderName)
    Next
End Sub

 Level.9の57〜60行目にあったサブフォルダ処理を、テキストファイル処理の最後に追加して、フォルダ情報が入ったオブジェクトsrcを、テキストファイル処理で使っているsubfに変更しただけです。

 いかがでしょうか?。これで、テキストファイルの処理が終わった後、同じフォルダ内にあるサブフォルダを探し、そのサブフォルダ名でまた自分自身(FileSrh関数)を呼び出す。すると、そのフォルダ内にあるテキストファイルを処理し、サブフォルダを検索し...と、なんだかうまく行きそうですね。

再帰処理(スクリプトの全体像)

 では、残りの部分を上記の続きにくっつけてみます。

テキストファイル処理関数の中にサブフォルダ処理を入れてみた
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
' --< テキストファイル処理関数 >-------------------------------------------------
' 指定のフォルダに格納された各ファイルを処理する
Sub FileSrh(fn)
    Set subf = fso.GetFolder(fn)    ' 引数に指定されたフォルダの情報を取得
    For Each FileName In subf.Files
        FileEx = fso.GetExtensionName(FileName)   ' ファイル名から拡張子を抜き出す
        If LCase(FileEx) = "txt" Then             ' 拡張子を小文字化してから比較
    
          ' テキストファイルの内容読み込み処理
            Set TextFile = fso.OpenTextFile(FileName) ' テキストファイルのオープン
            srhLine = FileName & ":" & vbNewLine      ' 検索結果格納用変数の初期化
            srhFlg = 0                                ' 検索ヒットフラグのクリア
            Do Until TextFile.AtEndOfStream
                tmpLine = TextFile.ReadLine           ' 1行読み込み
    
                If regEx.Test(tmpLine) Then           ' 指定の文字列を含むか?
                    srhLine = srhLine & TextFile.Line - 1 & ":" & tmpLine & vbNewLine
                                                      ' 読み込んだ行のマージ
                    srhFlg =1                         ' 検索がヒットしたので1にする
                End If
            Loop
    
            If srhFlg = 1 Then                        ' 指定の文字列が含まれてたら
                WScript.Echo srhLine
            End If
    
        End If
    Next

    ' 指定のフォルダに格納された各サブフォルダを処理する
    For Each FolderName In subf.Subfolders
        FileSrh(FolderName)
    Next
End Sub

' --< main >---------------------------------------------------------------------
Set fso = CreateObject("Scripting.FileSystemObject")

' フォルダ名入力処理
srhFol = ""
Do Until fso.FolderExists(srhFol)  ' 存在するフォルダ名が入力されるまで繰り返す
    tmpMsg = "検索フォルダを入力して下さい" & vbNewLine & _
             "終了時は""c""と入力して下さい。"
    srhFol = InputBox(tmpMsg,,"C:\script\tips") ' 対象フォルダの指定
    If LCase(srhFol) = "c" Then     ' cが入力されたらスクリプトを終了する
        WScript.Quit
    End If
Loop
Set src = fso.GetFolder(srhFol)               ' 対象フォルダの指定

' 検索文字入力処理
Set regEx = New RegExp                        ' 文字列検索用オブジェクトの作成
srhStr = InputBox("検索文字を入力して下さい",,"ファイル") ' 検索文字列の指定

regEx.Pattern = srhStr                        ' 検索文字列を検索パターンとして指定
regEx.Global = True                           ' 文字列全体を検索するように指定
regEx.IgnoreCase = True                       ' 大文字・小文字は検索に影響しない

' テキスト処理関数呼び出し
FileSrh(srhFol)

 基本的に残りの部分(Level.9の31〜55行目)をくっつけただけですが、44行目にあった「Set src = fso.GetFolder(srhFol)」の行は、サブフォルダ処理の為だけにあった行なので削除しました。

 繰り返しになりますが、サブフォルダ処理をテキストファイル処理の最後に移動して、src.Subfoldersをsubf.Subfoldersに変更。メイン処理内にあった、srcの定義は使わなくなったので削除。これだけです。

実行してみましょう

 さっそく実行したいところですが、実験環境は1階層だけなので、このままではうまく検索できたかどうか分かりません。

現状の実験環境

 そこで、こんな風にScriptフォルダとWindowsフォルダを移動して下さい。

エクスプローラでフォルダを移動

 準備が出来たら実行してみましょう。Level.9と同様コマンドラインから実行します。

level10.vbsファイルをコマンドラインから実行
C:\>cd c:\script
C:\script>cscript level10.vbs > kekka.txt
C:\script>notepad kekka.txt

 実行手順の説明はLevel.9でやっていますので、ここでは割愛させて頂きます。kekka.txtをメモ帳で開いたら、全てのフォルダ内にあるテキストファイルが検索対象になっているか確認してみましょう。

level10.vbsの実行結果をメモ帳で確認

 ふぅ...ようやっと、目的のスクリプトが完成しました。改めてこの60行のスクリプトを眺めて見て下さい。スクリプトを始めて間もない人が、いきなり最初からこの状態をスクリプトを作れと言われたら、投げ出してしまうかもしれませんね(笑。でも、10行足らずのスクリプトから徐々にステップアップして行くことで、何とかこのレベルのスクリプトを書けるようになったかと思います。

 私の力不足から理解し難い部分も多々あったかと思いますが、これからスクリプトを始めようとされている方の一助になれば幸いです。

 ...と、何やら最終回のようなことを言っておりますが、実はもう少々先があります。Level.10で一応目的の機能が実現できた訳ですが、この状態ですと、ユーザーインタフェースが良くないとは思いませんか?

 バッチ処理に組み込むにしては、ダイヤログボックスがいちいち出てきて邪魔をしますし、GUIなツールとしては見た目が美しくありません。

 そこで、次回はバッチ処理用にコマンドライン引数で検索フォルダと、検索文字を指定できるバージョンを紹介します。その後の回では、ユーザインタフェースにブラウザ(IE)を使って使い勝手を向上させて行きたいと思います。

 ということで、もう少々おつきあい下さい。



前ページへ   [ステップアップ WSHへ戻る]   次ページへ
-
※全ては自己責任でお願いします。
※当サイトに関するご連絡は tomomo_c@hotmail.com までお願いします。
最終更新日 2004.6.10