こんにちは!日々のファイル整理やバッチ処理に追われていませんか?
「あっちのフォルダ、こっちのフォルダの中に眠っている特定の拡張子のファイルを、まとめて一網打尽に処理したい……!」そんな現場の絶望的な叫びをスマートに解決するのが、今回テーマにする【再帰的フォルダ探索】です。
マクロの記録から一歩進んで、Windowsの標準機能だけで重厚な自動化を実現するVBScriptの世界へようこそ。ここをクリアすれば、あなたのスクリプト力は一段も二段も跳ね上がります。一緒にマスターしていきましょう!
—
1. なぜ「再帰(さいき)」が必要なのか?
フォルダの中にフォルダがあり、さらにその中にフォルダがある……。いわゆる「ネスト(階層)が深い構造」ですね。
人間の手でこれをやろうとすると、何重にも `For` ループを書かなければならず、階層が深くなったら太刀打ちできなくなります。
そこで登場するのが「再帰呼び出し(Recursive Call)」です。これは、「関数の中に、自分自身を呼び出すコードを仕込む」というプログラミングの魔法。
[メイン処理]
└─ 指定フォルダを渡す
└─ 中のファイルを処理
└─ サブフォルダを発見! ──(ここで自分自身をもう一度呼ぶ)──┐
└─ その中のファイルを処理 │
└─ さらにサブフォルダ発見! ────────────────┘ (無限ループに気をつけて!)
この仕組みを使えば、どれだけフォルダの階層が深くても、たった数十行のスマートなコードで完全に網羅することができます。
—
2. 実戦投入!堅牢な再帰的フォルダ探索スクリプト
それでは早速、現場でそのままコピペして使える実用的なコードをお見せします。
今回は例として、「指定したルートフォルダ以下にあるすべての `.txt` ファイルを探し出し、コンソールに出力する(あるいは何らかの処理を施す)」というロジックを組みます。
メモ帳を開き、以下のコードを貼り付けて `search_files.vbs` という名前で保存してください。
‘ ==============================================================================
‘ スクリプト名: RecursiveFileSearch.vbs
‘ 概要: FileSystemObjectを用いた再帰的フォルダ探索と特定拡張子の一括処理
‘ 著者: チーフアーキテクト先輩
‘ ==============================================================================
Option Explicit ‘ 宣言強制(タイポによるバグを根絶するエンジニアのたしなみ)
‘ — 定数定義 —
Const TARGET_FOLDER = “C:\Work\TargetFolder” ‘ 探索の起点となるフォルダパス
Const TARGET_EXT = “.txt” ‘ 処理対象の拡張子
‘ — メイン処理の実行 —
Main
Sub Main()
Dim fso
‘ FileSystemObjectの生成(OSのファイルシステムを触るためのパスポート)
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 起点フォルダの存在確認(ここを怠る初心者が多すぎるので注意!)
If fso.FolderExists(TARGET_FOLDER) Then
WScript.Echo “=== 探索を開始します: ” & TARGET_FOLDER & ” ===”
‘ 再帰処理を行うプロシージャを呼び出す
Call TraverseFolders(fso, fso.GetFolder(TARGET_FOLDER))
WScript.Echo “=== すべての処理が完了しました ===”
Else
WScript.Echo “エラー: 指定されたフォルダが存在しません -> ” & TARGET_FOLDER
End If
‘ オブジェクトの解放(メモリ管理の基本。お行儀よく片付けます)
Set fso = Nothing
End Sub
‘ ==============================================================================
‘ サブプロシージャ: フォルダを再帰的に巡回する核心ロジック
‘ ==============================================================================
Sub TraverseFolders(fso, currentFolder)
Dim file, subFolder
‘ 1. 【ファイル処理フェーズ】現在地にあるファイル群を舐め尽くす
For Each file In currentFolder.Files
‘ 拡張子が一致するかチェック(LCaseで大文字小文字を無視するのがプロの技)
If LCase(fso.GetExtensionName(file.Name)) = LCase(Replace(TARGET_EXT, “.”, “”)) Then
‘ ——————————————————————
‘ 【ここに目的の処理を書く】
‘ 例: リネーム、移動、ファイルの中身の書き換えなど
‘ ——————————————————————
WScript.Echo “発見・処理: ” & file.Path
End If
Next
‘ 2. 【再帰フェーズ】現在地にあるサブフォルダを一つずつ掘り下げる
For Each subFolder In currentFolder.SubFolders
‘ 自分自身(TraverseFolders)をもう一度呼び出す!これが再帰の魔法
Call TraverseFolders(fso, subFolder)
Next
End Sub
—
3. コードのキモをエンジニア目線で解説
ここがこのコードの「美味しい」ポイントです。初心者から一歩抜け出すための重要な知識を解説します。
① `Option Explicit` は命綱
VBScriptでは変数を宣言せずにいきなり使うことができますが、これは「タイプミスしたときに原因不明のバグで爆死する」という罠を生みます。先頭に `Option Explicit` を書くことで、変数宣言を強制し、安全なコードを書く習慣をつけましょう。
② 大文字・小文字の罠を `LCase` で防ぐ
Windowsの世界ではファイル名の「.TXT」と「.txt」は同じものとして扱われますが、プログラムの世界は厳格です。
`LCase(fso.GetExtensionName(file.Name))` と小文字に統一して比較することで、「あれ、拡張子は合っているのにヒットしないぞ?」という悲劇を防ぎます。
③ オブジェクトのライフサイクル(後始末)
VBScript(というかCOMコンポーネント)では、使い終わったオブジェクトを `Set fso = Nothing` で解放してあげるのが大人の作法です。メモリリークを防ぎ、OSのリソースを正しく解放するプログラミングの基本をここで身につけましょう。
—
4. 現場でやりがちな「陥りやすいエラー」と対策
最後に、この再帰的探索ロジックを組む際につまづきやすいポイントを伝授しておきます。
- 「アクセス拒否 (Permission Denied)」エラーが出る
- 原因: Windowsのシステムフォルダ(`C:\System Volume Information` など)や、他のユーザーの権限が必要なフォルダに踏み込んでしまったときにおこります。
- 対策: 本格的なツールにする場合は、`On Error Resume Next` を適切に挟むか、アクセス権のあるフォルダだけにターゲットを絞るガード文を入れましょう。
- 無限ループの恐怖は?
- 朗報: `FileSystemObject` の `SubFolders` はハードディスク上の実際のディレクトリ構造を忠実にたどるため、シンボリックリンクなどを悪用しない限り、プログラムが迷子になって無限ループ(スタックオーバーフロー)を起こす心配は基本的にありません。安心して使ってください。
—
まとめ
今回は `FileSystemObject` を使った再帰的フォルダ探索の極意をお伝えしました。
- 再帰呼び出しを使えば、どれだけ深い階層も怖くない。
- 拡張子の判定は `LCase` で安全に行う。
- Option Explicit と オブジェクトの解放 で堅牢なコードを書く。
ここをクリアできれば、VBScriptの基本はもうバッチリです!
日常の面倒なファイル整理や、一括データ変換などの自動化ツールをサクッと作れるエンジニアとして、ぜひ周りをあっと言わせてください。それでは、快適な自動化ライフを!
