PowerShellでファイルを検索するスクリプト

powershell7 コンピュータ
powershell7

ファイルのインデックスをSQLiteのテーブルとして保存しテーブルからファイルを検索するスクリプトです。

モジュールのインストール

Install-Module SQLite

スクリプト

ファイル名:PowerSearch.ps1

<#
.SYNOPSIS
ファイルを検索するスクリプト

.DESCRIPTION
要SQLite

.EXAMPLE
.\PowerSearch.ps1 -SearchWord "a%"

.OUTPUTS
PSCustomObject

.PARAMETER SearchWord
検索文字

.PARAMETER Update
インデクスを更新

.PARAMETER Help
ヘルプを表示

.LINK
関連URL

#>
using namespace System.Data.SQLite

param(
    [string]
    $SearchWord,
    [switch]
    $Update,
    [switch]
    $Help
)

if ($Help) {
    Get-Help $PSCommandPath
    Exit 1
}
# モジュールのインポート
Import-Module SQLite

# 検索対象のフォルダー
$SearchFolders = @("H:\ps1\PowerSearch\data")

# 最終更新日時ファイルのパス
$LastUpdatePath = Join-Path (Split-Path $PSCommandPath -Parent) "lastupdate.txt"

# ファイルデータベースのパス
$FileDBPath  = Join-Path (Split-Path $PSCommandPath -Parent) "files.db"

# コネクションオブジェクトの生成
$Connection = [SQLiteConnection]::new() | % {
    $_.ConnectionString = ("Data Source = {0}"-f $FileDBPath)
    $_.Open()
    $_
}

# テーブル作成
$Command = [SQLiteCommand]::new()
$Command.Connection = $Connection
$Command.CommandText = @"
CREATE TABLE IF NOT EXISTS files (
    full_name text,
    name text,
    lastwreite text,
    size long,
    primary key(full_name)
)
"@
$Command.ExecuteNonQuery() | Out-Null
# インデックスの作成
$Command.CommandText = @"
CREATE INDEX IF NOT EXISTS files_name ON files(name)
"@
$Command.ExecuteNonQuery() | Out-Null

# 最終更新日時ファイル作成
if (-not (Test-Path $LastUpdatePath)) {
    "1970/01/01 00:00" | Out-File -FilePath $LastUpdatePath
}
# 最終更新日時を保存
function SaveLastUpdate {
    # 1分前
    $dt = (Get-Date).AddMinutes(-1).ToString("yyyy/MM/dd HH:mm")
    $dt | Out-File -FilePath $LastUpdatePath
}
#SaveLastUpdate

# 最終更新日時を読み込み
function LoadLastUpdate {
    Get-Content -LiteralPath $LastUpdatePath
}
#LoadLastUpdate

# データベースを更新
function UpdateDB {
    param (
        [string]
        $fullName,
        [string]
        $name,
        [string]
        $lastwrite,
        [long]
        $size
    )
    $fullName = $fullName -replace "'", "''"
    $name = $name -replace "'", "''"

    $Command.CommandText = @"
SELECT COUNT(*) AS CNT FROM files WHERE full_name = '$fullName'
"@
    $rec = $Command.ExecuteReader()
    $cnt = $rec["CNT"]
    $rec.Close()

    if ($cnt -eq 0) {
        $Command.CommandText = @"
INSERT INTO files (full_name, name, lastwreite, size) values ('$fullName', '$name', '$lastwrite', $size)
"@
        Write-Host $Command.CommandText
    } else {
        $Command.CommandText = @"
UPDATE files SET name = '$name', lastwreite = '$lastwreite', size = $size WHERE full_name = '$fullName'
"@        
    }
    $Command.ExecuteNonQuery() | Out-Null
}

# テーブルの削除
function DeleteTable {
    param (
        [string]
        $fullName
    )
    $fullName = $fullName -replace "'", "''"
    $Command.CommandText = @"
DELETE FROM files WHERE full_name = '$fullName'
"@
    $Command.ExecuteNonQuery() | Out-Null
}
# インデクスの更新
function UpdateIndex {
    $dt = [DateTime](LoadLastUpdate)
    Write-Host ("前回更新:{0}以降のファイルを検索更新" -f $dt)
    SaveLastUpdate    
    $SearchFolders | ForEach-Object {
        $SearchFolder = $_
        Get-ChildItem -LiteralPath $SearchFolder -File -Recurse | Where-Object {
            $_.LastWriteTime -gt $dt
        } | ForEach-Object {
            UpdateDB $_.FullName $_.Name $_.LastWriteTime.ToString("yyyy/MM/dd HH:mm") $_.Length
        }
    }
}


# 検索文字
function SearchIndex {
    $deleteFiles = @()
    $Command.CommandText = @"
SELECT full_name, name, lastwreite, size FROM files WHERE name like '$SearchWord'
"@
    $rec = $Command.ExecuteReader()
    while ($rec.Read()) {
        $fullName = $rec['full_name']

        if (Test-Path -LiteralPath $fullName) {
            [PSCustomObject]@{
                "Name"=$rec['name']
                "Location"=(Split-Path $rec['full_name'] -Parent)
                "LastWrite"=$rec['lastwreite']
                "Size" = $rec['size']
            }
        } else {
            $deleteFiles += $fullName
        }

    }
    $rec.Close()
    $deleteFiles | ForEach-Object { DeleteTable $_ }
}

# 更新
if ($Update) {
    UpdateIndex
}

# 検索
if ($SearchWord -ne "") {
    SearchIndex
}

# コネクションを閉じる
$Connection.Close()

使い方

・検索フォルダの設定はスクリプト内の$SearchFoldersにパスをセット(複数指定可)
・インデックスの更新

.\PowerSearch.ps1 -Update

・検索

.\PowerSearch.ps1 -SearchWord "a%"

例は、aから始まるファイル名を検索します。
(%は任意の0文字以上の文字列、_は任意の1文字とマッチングします。)

コメント