【入門編】【階層検索】再帰呼び出し(Recursive)を用いたサブフォルダ配下の全ファイル網羅的検索スクリプト – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!今日も一歩、プログラミングの世界を深めていきましょう。

Excelのマクロ記録を卒業し、「もっと自由自在に、PC全体のファイルを操作したい」「自動化の幅を広げたい」と思い始めたあなたへ。今回は、VBScript(Visual Basic Scripting Edition)の真骨頂とも言える「ファイルシステムオブジェクト(FSO)を使った、サブフォルダ配下の全ファイル網羅的検索(再帰呼び出し)」について解説します。

「再帰(さいき)」と聞くと、なんだか難しそうに感じるかもしれません。しかし、ここをクリアすれば、VBScriptの基本はもちろん、プログラムがメモリをどう扱うかという「プロの視点」が身につきます。

一歩ずつ、優しく、かつ現場で一生使える「極限の書き方」を学んでいきましょう!

—

1. 「再帰呼び出し(Recursive)」を直感的に理解しよう

フォルダの中にフォルダがあり、その中にまたフォルダがあり……という「入れ子構造」をイメージしてください。

もしこれを、再帰を使わずに書こうとすると、「1階層目の処理」「2階層目の処理」「3階層目の処理」と、無限に続くプログラムを書かなければならなくなります。これは現実的ではありませんね。

ここで登場するのが「再帰呼び出し」です。

再帰呼び出しのイメージ図

再帰とは、「ある関数(手続き)の中で、自分自身をもう一度呼び出す」というテクニックです。

[検索関数( フォルダA )]
│
├── フォルダAのファイルを処理する
│
└── [サブフォルダ群を発見!]
│
├── [検索関数( サブフォルダA-1 )] ── 自分自身を呼び出す!
│ │
│ └── (さらに深くへ…)
│
└── [検索関数( サブフォルダA-2 )] ── 自分自身を呼び出す!

「フォルダを見つけたら、そのフォルダを対象にして、まったく同じ検索処理(自分自身)を呼び出す」というルールを決めておくだけで、どんなに深い階層であっても、自動的に最深部まで掘り進んでくれるのです。

—

2. 誰も教えてくれない「FSOとメモリ」の真実

VBScriptでファイルやフォルダを操作するときは、`Scripting.FileSystemObject`(通称FSO)というオブジェクトを使います。

ここで、マクロから脱却したばかりの人が最も陥りやすい「重大な罠」があります。それは、「再帰のループの中で、何度もFSOを作り直してしまう」ことです。

悪い例(アンチパターン)

‘ ※これはマネしないでください!
Sub SearchFolder(Folder)
‘ 関数が呼ばれるたびに、重たいFSOをメモリ上に新規作成している
Dim fso
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘…処理…
End Sub

再帰処理は、何百、何千回と繰り返されます。そのたびに`CreateObject`を実行すると、PCのメモリがあっという間に消費され、動作が極端に重くなったり、スクリプトが強制終了したりします。

プロの設計(ベストプラクティス)

FSOは「最初に1個だけ作って、それを全員で使い回す(参照を渡す)」のが鉄則です。
また、使い終わったオブジェクト変数は、必ず `Set 変数 = Nothing` で明示的に解放し、Windowsに「もう使わないよ」と教えてあげましょう。この「ライフサイクルの管理」ができるようになると、あなたのコードの信頼性は劇的に向上します。

—

3. 【完全版】サブフォルダ配下の全ファイルを網羅するスクリプト

それでは、実際に動くスクリプトを見てみましょう。
以下のコードをコピーして、メモ帳などに貼り付け、拡張子を `.vbs`(例: `file_search.vbs`)にして保存すれば、ダブルクリックで実行できます。

このコードは、指定したフォルダから、「特定の拡張子(例: .log や .txt)」を持つファイルをすべて探し出し、そのパスをテキストファイルに書き出す実戦的なスクリプトです。

Option Explicit

‘ =========================================================================
‘ メイン処理(ここからスタート)
‘ =========================================================================
Sub Main()
Dim fso, targetFolderPath, outputFilePath, outputFile

‘ — 【設定エリア】 —
‘ 探索したい親フォルダのパス(環境に合わせて書き換えてください)
targetFolderPath = “C:\TestFolder”
‘ 結果を出力するテキストファイルのパス
outputFilePath = “C:\TestFolder\search_result.txt”
‘ ———————-

‘ FSO(ファイルシステムオブジェクト)の生成(★スクリプト全体でこの1個だけを使う)
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 事前チェック:対象フォルダが存在するか
If Not fso.FolderExists(targetFolderPath) Then
MsgBox “対象フォルダが存在しません: ” & targetFolderPath, vbCritical, “エラー”
Set fso = Nothing
Exit Sub
End If

‘ 出力用のテキストファイルを開く(新規作成 / 上書きモード)
On Error Resume Next
Set outputFile = fso.CreateTextFile(outputFilePath, True, True) ‘ Unicode対応
If Err.Number <> 0 Then
MsgBox “出力ファイルの作成に失敗しました。パスを確認してください。” & vbCrLf & Err.Description, vbCritical, “エラー”
Set fso = Nothing
Exit Sub
End If
On Error GoTo 0

‘ ヘッダーの書き込み
outputFile.WriteLine “==================================================”
outputFile.WriteLine ” 検索開始時刻: ” & Now
outputFile.WriteLine ” 検索対象: ” & targetFolderPath
outputFile.WriteLine “==================================================”
outputFile.WriteLine “”

‘ — 再帰探索の実行 —
Dim parentFolder
Set parentFolder = fso.GetFolder(targetFolderPath)

‘ 再帰関数を呼び出す(FSOと出力ファイルオブジェクトを「参照渡し」で引き継ぐ)
Call RecursiveSearch(fso, parentFolder, outputFile)

‘ 完了処理
outputFile.WriteLine “”
outputFile.WriteLine “==================================================”
outputFile.WriteLine ” 検索終了時刻: ” & Now
outputFile.WriteLine “==================================================”

‘ ファイルを閉じる
outputFile.Close

‘ オブジェクトの明示的解放(美しい幕引き)
Set parentFolder = Nothing
Set outputFile = Nothing
Set fso = Nothing

MsgBox “検索が完了しました!” & vbCrLf & “結果: ” & outputFilePath, vbInformation, “完了”
End Sub

‘ =========================================================================
‘ 再帰探索を行うサブプロシージャ
‘ =========================================================================
Sub RecursiveSearch(ByRef fso, ByVal currentFolder, ByRef outputFile)
Dim file, subFolder

‘ — パターンA: 現在のフォルダ内にあるファイルを処理 —
‘ ※アクセス権がないフォルダ対策として、エラーハンドリングを局所的に導入
On Error Resume Next
For Each file In currentFolder.Files
If Err.Number = 0 Then
‘ 【条件指定】ここでは例として、拡張子が “.txt” または “.log” のファイルを抽出
Dim ext
ext = LCase(fso.GetExtensionName(file.Name))

If ext = “txt” Or ext = “log” Then
‘ 条件に合致したファイルのフルパスを出力ファイルに書き出す
outputFile.WriteLine file.Path & ” (” & file.Size & ” bytes)”
End If
Else
‘ アクセス拒否などのエラーが発生した場合はスキップ
Err.Clear
End If
Next
On Error GoTo 0

‘ — パターンB: サブフォルダを探索(ここで自分自身を呼び出す!) —
On Error Resume Next
For Each subFolder In currentFolder.SubFolders
If Err.Number = 0 Then
‘ サブフォルダを対象にして、自分自身(RecursiveSearch)を呼び出す(再帰)
Call RecursiveSearch(fso, subFolder, outputFile)
Else
‘ アクセス権のないシステムフォルダ(System Volume Informationなど)はスキップ
Err.Clear
End If
Next
On Error GoTo 0

‘ メモリリーク防止:ループ内で使い終わったローカル変数を解放
Set file = Nothing
Set subFolder = Nothing
End Sub

‘ スクリプトの実行エントリーポイント
Call Main()

—

4. コードの解説と「プロのこだわり」

このコードには、ただ動くだけではない「現場でトラブルを起こさないための知恵」が詰まっています。ポイントを絞って解説しますね。

① `ByRef`(参照渡し)によるFSOの使い回し

Sub RecursiveSearch(ByRef fso, ByVal currentFolder, ByRef outputFile)

関数の引数に `ByRef`(バイレフ)が付いています。これは「オブジェクトのコピーを作るのではなく、大元にある1つのオブジェクトをみんなで指し示そうね」という指定です。
これにより、メモリの消費を極限まで抑え、超高速に処理を行うことができます。

② `On Error Resume Next` の局所化(スコープ限定)

ファイル探索スクリプトを実際に業務で動かすと、高確率で「アクセス許可のないフォルダ」(Windowsのシステムフォルダや、他人の制限フォルダなど)にぶつかって強制終了します。
これを防ぐために、エラーが発生しても処理を止めずにスキップする記述をしています。

On Error Resume Next
For Each file In currentFolder.Files
…
On Error GoTo 0 ‘ エラー無視をすぐに解除する

ポイントは、「エラーを無視する範囲を最小限に抑える」ことです。全体でエラーを無視し続けると、予期せぬバグ(ディスク容量不足や書き込み失敗など)に気づけなくなってしまいます。

③ 拡張子判別のスマートさ

ext = LCase(fso.GetExtensionName(file.Name))

`LCase`関数を使って、取得した拡張子をすべて「小文字」に変換しています。これにより、`.TXT` や `.Txt` のように、大文字小文字が混ざったファイル名も漏れなくキャッチできます。細かいですが、実務でトラブルを防ぐための必須テクニックです。

—

5. 陥りやすいエラーと対策

エラー1:「書き込みできません」(エラー番号: 70)

  • 原因: 管理者権限がないフォルダに検索結果を出力しようとしたり、出力先ファイルがExcelなどで既に開かれている場合に発生します。
  • 対策: `outputFilePath` の保存先を、自分の「デスクトップ」や「ドキュメント」フォルダなど、確実に書き込み権限がある場所に変更してください。

エラー2:「スタック領域が不足しています」(エラー番号: 28)

  • 原因: 再帰呼び出しの階層が深すぎる(何百階層もある)、または無限ループに陥っている。
  • 対策: Cドライブ全体(`C:\`)などを指定すると、このエラーが発生することがあります。探索のスタート地点は、業務で使用するデータフォルダなど、必要な範囲に限定しましょう。

—

まとめ:ここをクリアすれば、VBScriptの基本はバッチリですよ!

お疲れ様でした!
今回学んだ「再帰呼び出し」と「オブジェクトのライフサイクル管理」は、VBScriptのみならず、VBA、JavaScript、Pythonなど、あらゆるプログラミング言語に共通する極めて重要な概念です。

  • 処理を共通化してスマートに掘り進む(再帰)
  • 重たいオブジェクト(FSO)は1つだけ作って使い回す(ByRef)
  • エラーが起きやすい場所(ファイルアクセス)だけを優しく保護する

この3つの思想を意識するだけで、あなたの書くコードは「素人のマクロ」から「プロのスクリプト」へと劇的に進化します。

ぜひ、身の回りのファイルの整理や、ログファイルの自動抽出などで試してみてくださいね。何かわからないことがあれば、いつでも先輩を頼ってください。応援しています!

タイトルとURLをコピーしました