0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ファイルコンテンツ検索_キーワード検索_コピペするだけで簡単

Last updated at Posted at 2025-09-13

■ 概要

これは業務(仕事)で、下記の場合に当てはまる場合にとても役に立つツールとして、自分のために残すツール作成キットである。
・入りたての現場で、右も左もわからないのに資料の中身から特定のキーワードを検索したい。
・大量のファイルの中から特定のキーワードを検索してその結果をファイルに出力させたい。
・オンプレミス環境でも自分のツールを作りたい。
・楽して簡単なツールを作成したい。
・勉強したい。

■ 作成ファイル一覧(文字コード「UTF-8」、「BOM」付き、改行コード「LF」で保存してください)

Search-Files.ps1 (PowerShellスクリプト本体)
config.json (動作設定ファイル)
README.txt (取扱説明書)
input_path.csv (検索対象フォルダ指定ファイル)
keywords.csv (検索キーワード指定ファイル)

1. PowerShellスクリプト (Search-Files.ps1)

<#
.概要
    指定されたフォルダ内を再帰的に検索し、キーワードを含むファイルの詳細情報を出力するスクリプト。

.説明
    設定ファイル(config.json)と入力CSV(input_path.csv, keywords.csv)に基づいて、テキスト、Word, Excelファイルの内容を検索。
    通常ログとエラーログ、検索結果を実行日時のタイムスタンプを付与したファイルとして自動生成。
    長いパスのファイルは一時フォルダにコピーしてから検索をしたのち、一時フォルダごと削除する。
    進捗バーが出力される。
    Microsoft Office製品の検索には、該当アプリケーションがインストールされている必要があります。
#>


# --- 初期設定 ---
$ErrorActionPreference = "Stop" # エラー発生時にスクリプトを停止(途中で止まらず進むと不具合原因が特定困難になるため)


# スクリプトの実行パスを取得
$scriptPath = $PSScriptRoot  # 実行しているスクリプト自身のパスを基準として、関連ファイルを探すために使用


# --- ファイルパス定義 ---
$configFile = Join-Path $scriptPath "config.json"         # 設定ファイル(検索条件、拡張子、除外フォルダ等)
$inputPathFile = Join-Path $scriptPath "input_path.csv"   # 検索対象のフォルダパスを格納したCSV
$keywordsFile = Join-Path $scriptPath "keywords.csv"      # 検索対象のキーワードを格納したCSV
$tempDir = Join-Path $scriptPath "temp_search_files"      # 長いパスに対応するための一時コピー格納フォルダ


# --- 出力ファイル名生成 ---
$timestamp = Get-Date -Format 'yyyy-MM-dd_HH-mm-ss'       # 出力ファイルの一意性を確保するために日時を付与
$outputFile = Join-Path $scriptPath "SearchResult_$timestamp.txt"  # 検索結果出力ファイル
$errorLogFile = Join-Path $scriptPath "ErrorLog_$timestamp.txt"    # エラー記録用ファイル
# 通常ログ出力ファイル
$normalLogFile = Join-Path $scriptPath "ProcessLog_$timestamp.txt" # 通常の進捗や開始終了ログを保存するファイル


# --- ログ出力用関数 ---
function Write-Log ($message, $filePath) {
    try {
        # 指定したログファイルに日付付きでメッセージを追記
        Add-Content -Path $filePath -Value "$(Get-Date -Format 'yyyy/MM/dd HH:mm:ss') - $message"
    } catch {
        # ログファイル自体が壊れている/権限が無い場合などに出力
        Write-Host "致命的なエラー: ログファイル '$filePath' に書き込めません。"
    }
}

# 進捗や開始終了時刻を出力(通常ログ用)
function Write-LogInfo ($message) {
    Write-Log $message $normalLogFile
}


# --- 入力ファイル・設定ファイルの読み込み ---
try {
    # 設定ファイルの読み込み(検索条件を格納)
    if (-not (Test-Path $configFile)) { throw "設定ファイル 'config.json' が見つかりません。" }
    $config = Get-Content $configFile -Raw | ConvertFrom-Json

    # 検索パスCSVの読み込み(対象ディレクトリ指定)
    if (-not (Test-Path $inputPathFile)) { throw "検索パス指定ファイル 'input_path.csv' が見つかりません。" }
    $searchPath = (Import-Csv $inputPathFile).Path
    if ([string]::IsNullOrEmpty($searchPath)) {
        throw "'input_path.csv' にパスが指定されていません。"
    }
    # 長いパスに対応するため、Windows固有の \\?\ パス形式で検証
    $longPathForTest = $searchPath
    if ($longPathForTest -notlike "\\*") { $longPathForTest = "\\?\$longPathForTest" }
    if (-not (Test-Path -LiteralPath $longPathForTest -PathType Container)) {
        throw "'input_path.csv' に指定されたフォルダパスが無効です: '$searchPath'"
    }

    # キーワードCSVの読み込み(複数キーワードをカンマ区切りで格納可能)
    if (-not (Test-Path $keywordsFile)) { throw "キーワード指定ファイル 'keywords.csv' が見つかりません。" }
    $keywords = (Import-Csv $keywordsFile).Keyword -split ',' | ForEach-Object { $_.Trim() }
    if ($keywords.Count -eq 0) { throw "'keywords.csv' からキーワードが読み込めませんでした。" }

} catch {
    # 初期化時点でのエラーは致命的 → エラーログに残して終了
    $errorMessage = "スクリプトの初期化に失敗しました: $($_.Exception.Message)"
    Write-Host $errorMessage -ForegroundColor Red
    Write-Log $errorMessage $errorLogFile
    exit 1
}


# --- 検索対象ファイルのリストアップ ---
Write-Host "検索対象ファイルの一覧を作成しています..."
try {
    # config.json から対象拡張子を取得してフィルタ用に整形
    $includePatterns = $config.include_extensions | ForEach-Object { $_.ToLower() }

    # 対象拡張子のファイルのみを効率的に取得(対象外は最初から無視)
    $targetFiles = Get-ChildItem -LiteralPath $searchPath -Recurse -File | Where-Object { $includePatterns -contains $_.Extension.ToLower() }

    # 除外フォルダに一致するものを正規表現で除去
    if ($config.exclude_folders.Count -gt 0) {
        $excludeRegex = ($config.exclude_folders | ForEach-Object { [regex]::Escape($_) }) -join '|'
        $targetFiles = $targetFiles | Where-Object { $_.FullName -notmatch $excludeRegex }
    }
    
    # ファイル件数を画面と通常ログに出力
    Write-Host "検索対象ファイル数: $($targetFiles.Count) 件"
    Write-LogInfo "検索対象ファイル数: $($targetFiles.Count) 件"
} catch {
    # ファイルリスト作成に失敗した場合
    $errorMessage = "ファイルリストの作成中にエラーが発生しました: $($_.Exception.Message)"
    Write-Host $errorMessage -ForegroundColor Red
    Write-Log $errorMessage $errorLogFile
    exit 1
}


# --- キーワードチェック関数 ---
function Check-Keywords {
    param(
        [string]$text,          # チェック対象文字列
        [array]$keywords,       # 検索キーワード
        [psobject]$config       # 設定内容(検索モード、論理条件など)
    )

    $matchCount = 0
    $compareOption = if ($config.case_sensitive) { "CaseSensitive" } else { "IgnoreCase" }
    
    if ([string]::IsNullOrWhiteSpace($text)) { return $false }

    # キーワードごとに一致判定
    foreach ($kw in $keywords) {
        $pattern = if ($config.search_mode -eq 'regex') { $kw } else { [regex]::Escape($kw) }
        if ($text -match $pattern) {
            $matchCount++
        }
    }

    # AND 条件: 全キーワード含む場合のみ一致
    # OR 条件: いずれかを含めば一致
    if ($config.search_logic -eq 'AND') {
        return $matchCount -eq $keywords.Count
    } else { # OR
        return $matchCount -gt 0
    }
}


# --- 検索処理 ---
$totalFiles = $targetFiles.Count
$processedCount = 0   # 処理済み件数
$foundCount = 0       # キーワードが見つかったファイル数

# 開始時刻の記録と画面/ログへの出力
$startTime = Get-Date
Write-Host "処理開始: $startTime"
Write-LogInfo "処理開始: $startTime"
$lastProgressTime = $startTime   # 最後に進捗を出力した時刻を記録


# Office COMオブジェクトの準備(Word/Excelを自動操作するため)
$wordApp = $null
$excelApp = $null

# 長いパス対応のため、一時フォルダを事前作成
if (-not (Test-Path -LiteralPath $tempDir)) { New-Item -Path $tempDir -ItemType Directory | Out-Null }

try {
    # 対象ファイルごとに処理を実施
    foreach ($file in $targetFiles) {
        $processedCount++
        $originalFilePath = $file.FullName.Replace('\\?\', '') # ログ出力用の見やすいパス
        $fileToProcessPath = $file.FullName # 実際の処理で使うパス
        $isTempFile = $false # 一時コピーを作ったかどうかのフラグ
        $fileExtension = $file.Extension.ToLower()

        # 進捗バーを出力
        Write-Progress -Activity "ファイルを検索中" -Status "$originalFilePath" -PercentComplete (($processedCount / $totalFiles) * 100)

        # 10分ごとに進捗ログを出力
        $now = Get-Date
        if (($now - $lastProgressTime).TotalMinutes -ge 10) {
            $elapsed = New-TimeSpan -Start $startTime -End $now
            Write-Host "[$now] 進捗: $processedCount / $totalFiles 件 (経過時間: $($elapsed.ToString()))"
            Write-LogInfo "進捗: $processedCount / $totalFiles 件 (経過時間: $($elapsed.ToString()), 時刻: $now)"
            $lastProgressTime = $now
        }

        $textContent = ""
        $results = @()

        try {
            # Word/Excel など長いパスで開けない場合 → 一時フォルダにコピーして処理
            if ($file.FullName.Length -gt 240 -and (@(".doc", ".docx", ".xls", ".xlsx", ".xlsm") -contains $fileExtension)) {
                $tempFileName = "$(Get-Random)- $($file.Name)"
                $tempFilePath = Join-Path $tempDir $tempFileName
                Copy-Item -LiteralPath $file.FullName -Destination $tempFilePath
                $fileToProcessPath = $tempFilePath
                $isTempFile = $true
            }

            # 拡張子に応じた処理
            switch ($fileExtension) {
                # --- テキストベースのファイル ---
                { @(".txt", ".csv") -contains $_ } {
                    Get-Content -LiteralPath $fileToProcessPath | ForEach-Object {
                        $lineNum = $_.ReadCount
                        if (Check-Keywords -text $_ -keywords $keywords -config $config) {
                            $results += "$originalFilePath`:$lineNum`:`$_"
                        }
                    }
                }

                # --- Microsoft Word ファイル ---
                { @(".doc", ".docx") -contains $_ } {
                    if ($null -eq $wordApp) { $wordApp = New-Object -ComObject Word.Application; $wordApp.Visible = $false }
                    # 読み取り専用でWordを開く
                    $doc = $wordApp.Documents.Open($fileToProcessPath, $false, $true)
                    for ($i = 1; $i -le $doc.Paragraphs.Count; $i++) {
                        $paragraph = $doc.Paragraphs.Item($i)
                        $text = $paragraph.Range.Text
                        if (Check-Keywords -text $text -keywords $keywords -config $config) {
                            $pageNum = $paragraph.Range.Information(3) # ページ番号取得
                            $results += "$originalFilePath`:$pageNum ページ`:`$($text.Trim())"
                        }
                    }
                    $doc.Close([ref]$false)
                }

                # --- Microsoft Excel ファイル ---
                { @(".xls", ".xlsx", ".xlsm") -contains $_ } {
                    if ($null -eq $excelApp) { 
                        $excelApp = New-Object -ComObject Excel.Application
                        $excelApp.Visible = $false
                        $excelApp.DisplayAlerts = $false
                    }
                    # 読み取り専用でExcelを開く
                    $workbook = $excelApp.Workbooks.Open($fileToProcessPath, $false, $true)
                    try {
                        foreach ($sheet in $workbook.Worksheets) {
                            # セルの内容を検索
                            if ($sheet.UsedRange.Count -gt 1) {
                                $cells = $sheet.UsedRange
                                foreach ($cell in $cells) {
                                    $text = $cell.Text
                                    if ([string]::IsNullOrWhiteSpace($text)) { continue }
                                    if (Check-Keywords -text $text -keywords $keywords -config $config) {
                                        $results += "$originalFilePath`:$($sheet.Name)`:$($cell.Address($false, $false))`:`$text"
                                    }
                                }
                            }
                            # コメントの内容を検索
                            foreach ($comment in $sheet.Comments) {
                                $text = $comment.Text()
                                if (Check-Keywords -text $text -keywords $keywords -config $config) {
                                    $results += "$originalFilePath`:$($sheet.Name)`:コメント($($comment.Parent.Address($false,$false)))`:`$text"
                                }
                            }
                        }
                    }
                    finally {
                        $workbook.Saved = $true
                        $workbook.Close($false)
                    }
                }
            }

            # 検索結果が見つかった場合の出力処理
            if ($results.Count -gt 0) {
                $foundCount++
                Write-Host "[$foundCount] キーワード発見: $originalFilePath" -ForegroundColor Green
                $results | ForEach-Object { Add-Content -Path $outputFile -Value $_ }
            }

        } catch {
            # ファイル処理中のエラーを記録
            $errorMessage = "ファイル処理エラー ($originalFilePath): $($_.Exception.Message)"
            Write-Host $errorMessage -ForegroundColor Yellow
            Write-Log $errorMessage $errorLogFile
        }
        finally {
            # 一時ファイルを削除
            if ($isTempFile) {
                Remove-Item -LiteralPath $fileToProcessPath -Force -ErrorAction SilentlyContinue
            }
        }
    }
}
finally {
    # --- クリーンアップ処理 ---
    Write-Host "クリーンアップ処理を実行しています..."
    if ($null -ne $wordApp) {
        $wordApp.Quit()
        [System.Runtime.InteropServices.Marshal]::ReleaseComObject($wordApp) | Out-Null
        Remove-Variable wordApp
    }
    if ($null -ne $excelApp) {
        $excelApp.Quit()
        [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excelApp) | Out-Null
        Remove-Variable excelApp
    }
    # 一時フォルダを削除
    if (Test-Path -LiteralPath $tempDir) {
        Remove-Item -Recurse -Force -LiteralPath $tempDir
    }
    # GCを強制してメモリ解放
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()

    # 終了時刻と経過時間を表示/ログ出力
    $endTime = Get-Date
    $elapsed = New-TimeSpan -Start $startTime -End $endTime
    Write-Host "処理終了: $endTime"
    Write-Host "処理時間: $($elapsed.ToString())"
    Write-LogInfo "処理終了: $endTime"
    Write-LogInfo "処理時間: $($elapsed.ToString())"
}



Write-Host "処理が完了しました。"
Write-Host "検索結果は '$outputFile' に保存されました。存在しなければ検索結果は対象なしです。"
if (Test-Path $errorLogFile) {
    Write-Host "エラーログは '$errorLogFile' に保存されました。存在しなければエラーなしです。"
}
Write-Host "処理ログは '$normalLogFile' に保存されました。"


2. 設定ファイル (config.json)

{
    "_comment": "--- このファイルでスクリプトの動作を細かく設定します ---",

    "search_logic": "OR",
    "_comment_search_logic": "キーワードの検索論理を指定します。'AND' (全てのキーワードを含む) または 'OR' (いずれかのキーワードを含む) を選択してください。",

    "search_mode": "literal",
    "_comment_search_mode": "検索モードを指定します。'literal' (単純な文字列として検索) または 'regex' (正規表現として検索) を選択してください。",

    "case_sensitive": false,
    "_comment_case_sensitive": "検索時に大文字と小文字を区別するかどうか。区別する場合は true, しない場合は false を設定してください。",
    
    "include_extensions": [
        ".txt", ".csv",
        ".doc", ".docx",
        ".xls", ".xlsx", ".xlsm"
    ],
    "_comment_include_extensions": "検索対象にしたいファイルの拡張子をリスト形式で指定します。ここに無い拡張子は完全に無視されます。",

    "exclude_folders": [
        ".git",
        "node_modules",
        "$RECYCLE.BIN",
        "System Volume Information"
    ],
    "_comment_exclude_folders": "検索対象から除外したいフォルダ名をリスト形式で指定します。この名前を含むフォルダは再帰的に無視されます。"
}

3. 取扱説明書 (README.txt)

============================================================
 高機能ファイル内テキスト検索スクリプト README
============================================================

このツールは、指定したフォルダ内にある複数のファイルを横断的に検索し、
キーワードが含まれるファイルの場所と内容を特定するためのPowerShellスクリプトです。

------------------------------------------------------------
■ ファイル構成
------------------------------------------------------------
このツールを使用するには、以下のファイルがすべて同じフォルダに存在する必要があります。

- Search-Files.ps1  : スクリプト本体
- config.json       : 動作を設定するファイル
- input_path.csv    : 検索を開始するフォルダを指定するファイル
- keywords.csv      : 検索したいキーワードを指定するファイル
- README.txt        : この説明書

------------------------------------------------------------
■ 準備 (スクリプト実行前に行ってください)
------------------------------------------------------------

1. 【必須】検索対象フォルダの指定
   `input_path.csv` をメモ帳などのテキストエディタで開き、
   検索を開始したいフォルダのフルパスを1行だけ記述してください。

   (例)
   Path
   C:\Users\YourName\Documents

2. 【必須】キーワードの指定
   `keywords.csv` をテキストエディタで開き、検索したいキーワードを
   カンマ(,)区切りで記述してください。

   (例)
   Keyword
   プロジェクトA,請求書,2025年

3. 【任意】動作設定の変更
   `config.json` をテキストエディタで開くと、検索の細かい動作を
   変更できます。各項目の意味はファイル内のコメントを参照してください。
   (AND/OR検索の切り替え、正規表現の使用、大文字/小文字の区別など)

------------------------------------------------------------
■ 実行方法
------------------------------------------------------------

1. PowerShellを起動します。
   (Windowsのスタートメニューを右クリックし、「ターミナル」や「Windows PowerShell」を選択)

2. `cd` コマンドを使い、本スクリプトが保存されているフォルダに移動します。
   (例) cd C:\Tools\FileSearch

3. 以下のコマンドをコピー&ペーストして実行します。

   powershell.exe -ExecutionPolicy Bypass -File ".\Search-Files.ps1"

   ※「-ExecutionPolicy Bypass」は、PCのセキュリティ設定でスクリプトが
     ブロックされるのを一時的に回避するためのおまじないです。

------------------------------------------------------------
■ 実行結果
------------------------------------------------------------

スクリプトを実行したフォルダに、以下のファイルが自動的に作成されます。

- SearchResult_日付_時刻.txt
  キーワードが見つかった場合、ファイルの情報が記録されます。
  フォーマット: `ファイルフルパス:場所:キーワードを含む行やシート、セルの内容`

- ErrorLog_日付_時刻.txt
  処理中に何らかのエラー(ファイルが開けない等)が発生した場合に、
  その詳細が記録されます。

------------------------------------------------------------
■ 正規表現 (RegEx) の使い方
------------------------------------------------------------
`config.json` の `search_mode` を `"regex"` に変更すると、
キーワードとして正規表現パターンを使用できます。

正規表現は、文字列の「パターン」を表現するための特殊な記法です。
プログラマーにとって非常に強力なツールとなります。

【一般的な正規表現パターンの例】

- 電話番号 (日本形式): `\d{2,4}-\d{2,4}-\d{4}`
  (例: 03-1234-5678, 090-1234-5678 にマッチ)

- 郵便番号 (日本形式): `\d{3}-\d{4}`
  (例: 123-4567 にマッチ)

- メールアドレス: `[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}`
  (一般的なメールアドレスの形式にマッチ)

- 日付 (yyyy-mm-dd形式): `\d{4}-\d{2}-\d{2}`
  (例: 2025-09-13 にマッチ)

※ `keywords.csv` に上記パターンを記述して検索してみてください。

------------------------------------------------------------
■ 注意事項
------------------------------------------------------------
- WordやExcelファイルを検索するには、お使いのPCにMicrosoft Officeが
  インストールされている必要があります。
- 非常に多くのファイル(数十万件以上)を対象にすると、処理に時間が
  かかる場合があります。
- スクリプトの改造は自己責任でお願いいたします。

4. 検索対象フォルダ指定ファイル (input_path.csv)

Path
C:\temp\search_target

5. 検索キーワード指定ファイル (keywords.csv)

Keyword
サンプル,テスト,見積書

■ 実行手順

上記の5つの内容を、それぞれテキストファイルとして同じフォルダに保存してください。
input_path.csv と keywords.csv を、あなたの目的に合わせて編集します。
必要であれば config.json の設定を変更します。
README.txt に記載されている「実行方法」に従って、PowerShellからスクリプトを実行してください。


下記は業務的なドキュメント

高機能ファイル内容検索スクリプト ドキュメント

📝 概要

このスクリプトは、指定されたフォルダ配下のファイルを再帰的に検索し、
config.json で指定された拡張子のファイルに対して
keywords.csv のキーワードを含むかどうかを調査します。

  • 結果は SearchResult_yyyy-MM-dd_HH-mm-ss.txt に保存
  • 進捗や開始終了時刻は ProcessLog_yyyy-MM-dd_HH-mm-ss.txt に保存
  • エラーは ErrorLog_yyyy-MM-dd_HH-mm-ss.txt に保存

📂 ファイル構成

/Search-Files.ps1       # スクリプト本体
/config.json            # 検索設定ファイル
/input_path.csv         # 検索開始フォルダを指定
/keywords.csv           # 検索キーワードを指定
/README.md              # 説明書

🔄 処理フロー図


⚙️ 実運用手順書

1. 検索対象フォルダを指定

input_path.csv を編集して、検索開始したいフォルダを指定します。

Path
C:\Users\YourName\Documents

2. 検索キーワードを指定

keywords.csv を編集して、検索したい単語をカンマ区切りで記述します。

Keyword
プロジェクトA,請求書,2025年

3. 設定ファイルを確認

config.json で検索条件を設定します。

例:

{
  "search_logic": "OR",
  "search_mode": "literal",
  "case_sensitive": false,
  "include_extensions": [".txt", ".csv", ".docx", ".xlsx"],
  "exclude_folders": ["node_modules", ".git"]
}
  • search_logic: AND / OR
  • search_mode: literal / regex
  • case_sensitive: 大文字小文字を区別するか

4. 実行方法

PowerShell を起動し、スクリプトのあるフォルダに移動して以下を実行します。

powershell.exe -ExecutionPolicy Bypass -File ".\Search-Files.ps1"

5. 出力ファイルの確認

  • SearchResult_xxx.txt → キーワード一致結果
  • ProcessLog_xxx.txt → 開始・進捗・終了ログ
  • ErrorLog_xxx.txt → エラー内容

📊 ログ出力仕様

  • 開始時刻: スクリプト処理開始時に記録
  • 進捗: 10分ごとに件数・経過時間・時刻を記録
  • 終了時刻と経過時間: 処理終了後に記録
  • 検索対象件数: 対象拡張子のみをカウント

⚠️ 注意事項

  • Word/Excel 検索には Microsoft Office が必要です
  • 数十万件規模のファイル検索は時間がかかることがあります
  • 大量データ検索時はログを参照して進捗を確認してください

✅ まとめ

このスクリプトは 業務での文書横断検索 に利用でき、

  • 進捗ログ
  • エラーログ
  • 検索結果の分離保存

が可能なため、監査対応・トラブル調査などに有効です。


下記は異なる形の詳細ドキュメント「技術解説(コードの詳細解説付き)」

PowerShellで作る高機能ファイル内容検索スクリプト【技術解説】

📝 概要

本記事では、PowerShell で実装した フォルダ内ファイル横断検索スクリプト の仕組みを技術的に解説します。

  • 指定フォルダを再帰的に検索
  • config.json で指定された拡張子だけを対象に
  • keywords.csv に記載されたキーワードを検出
  • 検索結果・通常ログ・エラーログをファイル出力

進捗ログは 10分ごと に出力され、終了時には経過時間も記録されます。
大規模検索や監査ログ用途に利用可能です。


🔄 処理フロー図(Mermaid)


⚙️ ファイル構成

Search-Files.ps1   # スクリプト本体
config.json        # 検索設定
input_path.csv     # 検索開始フォルダを指定
keywords.csv       # 検索キーワードを指定
README.md          # 利用説明書

💡 コード解説

ここではスクリプトの主要部分を抜粋して解説します。
(フルコードはリポジトリ参照)


1. 初期設定

$ErrorActionPreference = "Stop"
$scriptPath = $PSScriptRoot
  • エラー発生時に即停止する設定(エラーを握りつぶすと誤検出につながるため)
  • $PSScriptRoot はスクリプト自身の場所を取得し、関連ファイル探索に利用

2. 出力ファイル名の生成

$timestamp = Get-Date -Format 'yyyy-MM-dd_HH-mm-ss'
$outputFile = Join-Path $scriptPath "SearchResult_$timestamp.txt"
$errorLogFile = Join-Path $scriptPath "ErrorLog_$timestamp.txt"
$normalLogFile = Join-Path $scriptPath "ProcessLog_$timestamp.txt"
  • 検索結果・エラー・通常ログを別々に保存
  • ファイル名に タイムスタンプ付与 → 実行ごとに一意化

3. 設定ファイル読み込み

$config = Get-Content $configFile -Raw | ConvertFrom-Json
$keywords = (Import-Csv $keywordsFile).Keyword -split ',' | ForEach-Object { $_.Trim() }
  • config.json → 検索ロジック・拡張子・除外フォルダを設定
  • keywords.csv → キーワードを読み込み、カンマ区切り対応

例: config.json

{
  "search_logic": "OR",
  "search_mode": "literal",
  "case_sensitive": false,
  "include_extensions": [".txt", ".csv", ".docx", ".xlsx"],
  "exclude_folders": ["node_modules", ".git"]
}

4. 検索対象ファイルの抽出

$includePatterns = $config.include_extensions | ForEach-Object { $_.ToLower() }
$targetFiles = Get-ChildItem -LiteralPath $searchPath -Recurse -File | 
               Where-Object { $includePatterns -contains $_.Extension.ToLower() }
  • 最初から拡張子でフィルタすることで進捗表示の実態と一致
  • 除外フォルダは正規表現で除去

5. キーワード一致関数

function Check-Keywords {
    param($text, $keywords, $config)

    foreach ($kw in $keywords) {
        $pattern = if ($config.search_mode -eq 'regex') { $kw } else { [regex]::Escape($kw) }
        if ($text -match $pattern) { return $true }
    }
    return $false
}
  • 検索モードに応じて正規表現/リテラル検索を切替
  • AND / OR 条件は設定ファイルで指定可能

6. 進捗管理とログ出力

$startTime = Get-Date
$lastProgressTime = $startTime

foreach ($file in $targetFiles) {
    $processedCount++
    $now = Get-Date
    if (($now - $lastProgressTime).TotalMinutes -ge 10) {
        $elapsed = New-TimeSpan -Start $startTime -End $now
        Write-Host "[$now] 進捗: $processedCount / $totalFiles 件 (経過: $elapsed)"
        Write-LogInfo "進捗: $processedCount / $totalFiles 件 (経過: $elapsed)"
        $lastProgressTime = $now
    }
}
  • 10分ごとに進捗件数と経過時間を出力
  • 大規模処理でも進捗を定期的に把握可能

7. Word / Excel の扱い

$wordApp = New-Object -ComObject Word.Application
$excelApp = New-Object -ComObject Excel.Application
$excelApp.DisplayAlerts = $false
  • COM オブジェクトを利用して Word / Excel を直接開く
  • 長いパスは一時フォルダにコピーして処理

8. 終了処理

$endTime = Get-Date
$elapsed = New-TimeSpan -Start $startTime -End $endTime
Write-Host "処理終了: $endTime"
Write-Host "処理時間: $elapsed"
Write-LogInfo "処理終了: $endTime"
Write-LogInfo "処理時間: $elapsed"
  • 終了時刻と経過時間を表示・ログに記録
  • COMオブジェクト解放 & 一時フォルダ削除でリソースリーク防止

⚠️ 注意事項

  • Microsoft Office が必須(Word/Excel 検索用)
  • 数十万件以上のファイルを対象にすると時間がかかる場合あり
  • 大規模検索では進捗ログを参照して進行を把握する

✅ まとめ

このスクリプトは業務用に必要な機能を実装済みです。

  • 検索対象拡張子のフィルタリング
  • 進捗ログ(10分ごと)
  • 検索結果・通常ログ・エラーログの分離保存
  • Word/Excel も横断検索可能

👉 ファイル監査や内部調査、ナレッジ検索基盤として応用可能です。


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?