PowerShellで、特定のフォルダーにあるExcelファイルから特定の検索ワードを用いた検索を行い、結果の一覧をcsvに出力する

複数のエクセルファイルの中から、特定のキーワードを用いて検索をかける。
この時、対象のファイルが少ない場合は手動で見てまわる事も、まぁ少数でも苦行なのですが、
多数の設計書の中からひたすら検索をかけて、検索結果をまた別のエクセルにまとめて
とにかくエクセルをたくさん触らせたい！みたいなお仕事を度々頂きます。
勉強になりますね。

あんまり勉強になるので、勉強がてらスクリプトにやってもらうことにしました。
オートシェイプの中は未対応なのですが、大好きなCtrl+Fでも見てないので、
この際無視することにしました。

```PowerShell:searchExcel.ps1
Param(
    $targetDir ,
    $searchWord ,
    $resultCsvFileName
)

if($targetDir -eq $null) {
    '-targetDir オプションを指定して、検索対象のフォルダーを指定してください。'
    return
}

if($searchWord -eq $null) {
    '-searchWord オプションを指定して、検索対象のキーワードを指定してください。'
    return
}

if($resultCsvFileName -eq $null) {
    $resultCsvFileName = Split-Path $myInvocation.MyCommand.Path -Parent
    $resultCsvFileName = Join-Path $resultCsvFileName "defaultResultFile.csv"
}

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false

$datas = New-Object System.Collections.ArrayList


Get-ChildItem $targetDir -Recurse -Include "*.xlsx" , "*.xls" | ForEach-Object {

    $fullName = $_.FullName
    $fullName

    $fileOpen = $true

    try {
        $workBook = $excel.Workbooks.open($fullName)
    } catch {
        Write-Host "$fullName のオープンに失敗しました。このファイルに対する処理はスキップします。" -ForegroundColor Red
        $fileOpen = $false
    }

    if($fileOpen) {
        $workBook.WorkSheets | ForEach-Object {
            $sheet = $_
            $sheetName = $sheet.Name
            $first = $result = $sheet.Cells.Find($searchWord)
            while ($result -ne $null) {
                $data = New-Object psobject | Select-Object FullName , SheetName , Column , Row , Value2 , Formula
                $data.FullName = $fullName
                $data.SheetName = $sheetName
                $data.Column = $result.Column
                $data.Row = $result.Row
                $data.Value2 = $result.Value2
                $data.Formula = $result.Formula
    
                [void]$datas.Add($data)
                $result = $sheet.Cells.FindNext($result)
    
                try {
                    $result.Address() | Out-Null
                } catch {
                    break
                }

                if(${result}.Address() -eq ${first}.Address()) {
                    break
                }
            }
        }
        [void]$workBook.close()
    }
}

$sheet = $null
$excel.quit()
[System.GC]::Collect([System.GC]::MaxGeneration)

$resultCsvFileNameParent = Split-Path $resultCsvFileName -Parent
if((Test-Path $resultCsvFileNameParent) -eq $false) {
    New-Item -ItemType Directory $resultCsvFileNameParent
}

$datas | Export-Csv $resultCsvFileName -Encoding Default -NoTypeInformation
```
検索部分の処理は、こちらを参考にさせていただいております。
[PowerShellで複数のExcelファイルを一括検索する](https://qiita.com/nejiko96/items/b423e2dda90181ef524e)


実行時には、下記の引数が必要になります。

|変数名               |指定内容                   |例                  |
|:-------------------|:--------------------------|:-------------------|
| -targetDir         | 検索対象のフォルダー名      | C:\temp            |
| -searchWord        | 検索キーワード             | test               |
| -resultCsvFileName | 結果を格納するCSVファイル名 | C:\temp\result.csv |

（上記の例で実行すると、C:\temp配下のフォルダー内にある.xlsx/.xlsファイルを開いて、
　test 文字列を検索し、その結果をC:\temp\result.csv に書き出します。）

出来上がったcsvファイルには下記の情報が含まれます。

|FullName|SheetName|Column|Row|Value2|Formula|
|:------:|:-------:|:----:|:-:|:----:|:-----:|
|ファイルのフルパス(含むファイル名)|シート名|列|行|Value2(表示上の値)|計算式|
上記の情報からValue2とかFormulaとかを書き換えて、書き戻すことによって
単純な置換だと対応しきれないようなケースを処理したりしたいわけです。
なので、このcsvを同じように一覧舐めて、FullNameを元にエクセルを開いて
sheetNameのcolumn,rowのValue2とかFormulaとかを書き換えるスクリプトが
欲しいのですが、また今度にします。

読み取り専用のチェックとかパスワードとか、ファイル開くときに出てくる
ポップアップについては対応していないので、必要に応じて修正が必要になるかと思います。