PowerShellでExcelファイル内の文字列を一括検索、再び

powershell コンピュータ
powershell

スクリプト

ファイル名:XlsxSearch.ps1

<#
.SYNOPSIS
 エクセルファイル(.xlsx)から文字を検索する。
.DISCRIPTION
 .xlsxファイルのパスをパイプラインで渡す。
 -Keywordで検索文字を指定する
#>
Param(
    [string]$Keyword,
    [switch]$Help
)

Add-Type -AssemblyName System.IO.Compression.FileSystem

function XlsxSearch
{
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [string[]]
        $files,
        [Parameter(ValueFromPipeline=$false,Mandatory=$true)]
        [string]
        $Keyword
    )
    begin {}
    process
    {
        #Write-Host ("Keyword:{0}" -f $Keyword)
        foreach ($file in $files)
        {
            #Write-Host ("File:{0}" -f $file)
           if ($zip = [System.IO.Compression.ZipFile]::OpenRead($file)) {
                try {
                    $words = {}
                    if ($t = $zip.GetEntry("xl/sharedStrings.xml"))
                    {
                        if ($fs = $t.Open()) {
                            $sr = [System.IO.StreamReader]::new($fs)
                            $sharedStrings = [xml]($sr.ReadToEnd())
                            $words = $sharedStrings.sst.si.t
                        }
                    }

                    $zip.Entries | ? {
                        $_.FullName -match "^xl/worksheets/.+\.xml$"
                    } | % {
                        $name = $_.Name
                        if ($fs = $zip.GetEntry($_.FullName).Open()) {
                            $sr = [System.IO.StreamReader]::new($fs)
                            $xmlDoc = [xml]($sr.ReadToEnd())
                            $xmlDoc.worksheet.sheetData.row | % {
                                $_.c | ? { $_.t -eq "s" } | ? { $words[$_.v] -match $Keyword } | % {
                                    [PSCustomObject]@{
                                        File = $file
                                        Sheet = $name
                                        Cell = $_.r
                                        Value = $words[$_.v]
                                    }
                                }
                            }
                        }
                    }
                } finally {
                    $zip.Dispose()
                }
            }
        }
    }
    end {}
}


$args = @($input)

if ($Help -Or ( -Not $OutDir -And $args.Count -eq 0))
{
    Get-Help $PSCommandPath
    Exit 1
}

if ($args.Count -gt 0)
{
    $args | XlsxSearch -Keyword $Keyword
}

使い方

ls ~\Desktop\*.xlsx | % {$_.FullName } | .\XlsxSearch.ps1 -Keyword "日本"

結果

File                                       Sheet      Cell Value
----                                       -----      ---- -----
C:\Users\karet\Desktop\202303171559.xlsx sheet1.xml B1   日本電産 
文字列にのみ検索可能、数値は未対応。
テストを殆どしていないので、ご利用する場合はプログラム修正が必須とお考え下さい。
前作はExcelがインストールされている必要がありますが、今作はインストールされていないPCでも動くはず。
PowerShellでExcelファイル内の文字列を一括検索
指定ディレクトリ下のExcelファイル(.xlsx)内に指定した文字列がある場合、ファイル名、シート名、セルの座標、セルの値を返すスクリプトです。 沢山あるExcelファイルから特定の文字を含むファイルを探すことが出来ると便利かと作ってみま...

説明

.xlsx(zip)中のファイルを覗いてみたところ、xlディレクトリに各シートがsheet1.xml,sheet2.xml…という風に.xml形式で保存されていました。その.xmlファイルを開いてみると各セルの内容が保存されているようです。ただセル内にセットした文字列は見当たりません。文字列を入力したセルには数値がセットされていました。調べたところ、この数値はxl/sharedStrings.xml内にある文字列の順番に対応するようです。ファイル名がsharedStringですので各シートで扱う文字列を共有することでファイルサイズを小さくするための辞書データだと思われます。
調べた情報はこの程度ですが、この情報を材料にPowerShellスクリプトを組んでみました。
エラー処理を省いていますので文字を含まない.xlsxファイルでエラーが出ています。

また、ファイルの指定はls(Get-ChildItem)で検索した結果をパイプラインで渡すことを想定しています。
その割にlsの結果を直接渡すコードが省かれており、一度%(Foreach-Object)でFullPathプロパティを取り出しそれを渡しています。この文書を書いている時間でコードを直せばよいのですが、そのうち修正出来ればと考えております。
同様に単一ファイルを引数に取る機能も付けるべきだとは思うのですが、そちらも省かれています。

実用する場合はそのあたりを修正する必要があると思います。

コメント