Firefoxのプロファイルフォルダ以下の全ての*.sqliteを最適化するPowershellスクリプト

久しぶりの更新。しばらく魂の抜けたような生活をしておりました。ぼちぼちGreasemonkeyの方も修正しなきゃなぁ。

というわけで,タイトルの通りのスクリプトを書いてみました。
以下の内容をコピペして「fx_optimize_sqlite.ps1」ってな感じの名前で保存してください。

param ([switch]$force)

$appProfDir = join-path $env:appdata "Mozilla\Firefox\Profiles"
$localProfDir = join-path $env:localappdata "Mozilla\Firefox\Profiles"

# http://csharper.blog57.fc2.com/blog-entry-206.html
function global:Invoke-Process
{
    param ([string] $processPath, [string]$processArgs, [int]$timeoutMilliseconds = [System.Threading.Timeout]::Infinite)
    trap
    {
        if ($() -ne $process)
        {
            $process.Dispose();
        }
        break;
    }
   
    if ([String]::IsNullOrEmpty($processPath)) { throw "引数 processPath が null または空の文字列です。"; }
    $process = New-Object "System.Diagnostics.Process";
    $process.StartInfo = New-Object "System.Diagnostics.ProcessStartInfo" @($processPath, $processArgs);
    $process.StartInfo.WorkingDirectory = (Get-Location).Path;
    $process.StartInfo.RedirectStandardOutput = $True;
    $process.StartInfo.RedirectStandardError = $True;
    $process.StartInfo.UseShellExecute = $False;
    $process.Start() | Out-Null;
   
    $message = $process.StandardOutput.ReadToEnd();
    $errorMessage = $process.StandardError.ReadToEnd();
    $complete = $process.WaitForExit($timeoutMilliseconds);
    if (!$complete)
    {
        $process.Kill();
    }
   
    $result =
        @{
            "IsSuccess" = ($process.ExitCode -eq 0);
            "IsComplete" = $complete;
            "Message" = $message;
            "ErrorMessage" = $errorMessage;
        };
    $process.Dispose();
   
    return $result;
}

function parse-dir($path) {
    write-host "$path";
    get-childitem $path -force | where {
        $_.Mode -match "^d"
    } | foreach {
        $profName = (split-path $_ -leaf).split(".", 2)[1];
        write-host "  * $profName";
        get-childitem $_.fullname -filter "*.sqlite" | where {
            $bak = "$($_.fullname).bak";
            $force -or -not (test-path $bak) -or ($_.lastwritetime -gt (get-item $bak).lastwritetime)
        } | foreach {
            $file = $_;
            $bakPath = "$($file.fullname).bak";
            write-host "    + $($file.name): " -nonewline;
            copy-item $file.fullname $bakPath;
            $bak = get-item $bakPath;
            $res1 = Invoke-Process "sqlite3" "`"$($file.fullname)`" vacuum";
            $res2 = Invoke-Process "sqlite3" "`"$($file.fullname)`" reindex";

            if($res1.IsSuccess -and $res2.IsSuccess) {
                # renew fileinfo (filesize)
                $file = get-item $file.fullname
                write-host "optimized" -foregroundcolor "blue";
                $diff = 100 * (1 - $file.length / $bak.length);
                write-host ("        {0:N0} => {1:N0} bytes ({2:N2})%" -f
                        ($bak.length, $file.length, $diff));
                $bak.set_lastwritetime($(get-date));
            } else {
                write-host "error" -foregroundcolor "red";
                write-host "         vacuum: " -nonewline;
                write-host $res1.ErrorMessage -foregroundcolor "red" -nonewline;
                write-host "        reindex: " -nonewline;
                write-host $res2.ErrorMessage -foregroundcolor "red" -nonewline;
                $bak.set_lastwritetime($file.lastwritetime - 10)
            }
        };
    };
    write-host "";
}

parse-dir $appProfDir
parse-dir $localProfDir

必要な物

sqlite3をどこからか入手してきてパスを通しておいてください。

使い方

powershell上でこのファイルを保存したディレクトリに移動して以下を実行すればOK

> .\fx_optimize_sqlite.ps1

これで「%APPDATA%\Mozilla\Firefox\Profiles」と「%LOCALAPPDATA%\Mozilla\Firefox\Profiles」以下の「*.sqlite」ファイルが最適化(vacuum, reindex)されます。

デフォルトの動作だと,前回の最適化から更新されたファイルのみを最適化します。強制的に全てのファイルを最適化したい場合,

.\fx_optimize_sqlite.ps1 -force

のように,「-force」スイッチを付けて実行してください。

便利な使い方

powershell c:\path\to\script\fx_optimize_sqlite.ps1」なんていうショートカットを作っておくと,クリック一発で最適化ができて便利です。

スクリーンショット


こんな感じの実行結果が得られます。
頭に "*" が付いている行がプロファイル名を表していて,その下の "+" が付いている行が実際に最適化を行ったファイルの名前です。"*" の行の下に "+" の行が無いプロファイルは,最適化を行う必要が無かったということを表しています。
最適化に成功した場合はファイルサイズの変化が表示されます。最適化を頻繁に行ってもあんまりサイズは縮まない,ということがよくわかります。
Firefox起動中に最適化を行おうとしてエラーが起きた場合,赤字でエラーメッセージが表示されます。

その他

スクリプト中の「Invoke-Process」関数はC#と諸々 PowerShell でプロセスを実行する関数の物を利用させていただきました。