タイダログ

もっと怠けますか? (y/n)

PowerShell でフォルダサイズの一覧を取得する:3.0+1.0

さらば、全てのフォルダのプロパティを見る作業。

4部構成の最終作。長編です。

この話の続きです。

taidalog.hatenablog.com

taidalog.hatenablog.com

taidalog.hatenablog.com

これまでの「PowerShell でフォルダサイズの一覧を取得する」

:1.0 ではフォルダサイズの一覧を取得するのに PowerShell を使ってみました。私が創り出した最初の PowerShell スクリプトです。

:2.0 で、:1.0 のスクリプトを1年越しに書き直し、one-liner 版と function 版を作りました。

:3.0 で、取得したフォルダサイズを CSV ファイルに出力して再利用できるようにしました。

そして今回が最終作。効率化を求めるタイダログの物語は、どこへと続くのか。

右クリックから実行できるようにする

ある時、ふと思いました。「いちいちコンソールを開くより、右クリックで使えた方が楽なのでは?」怠惰だからね。

そんなわけで、対象のフォルダを右クリックして、コンテキストメニューからスクリプトを実行できるようにしましょう。

スクリプトを用意する

今回必要なものは以下の2つです。

  1. フォルダサイズの一覧を取得する function (.psm1)
  2. function を呼び出すスクリプト (.ps1)

適当なフォルダを作り、そこに上の2つを保存します。今回はドキュメントフォルダ内に "get_the_list_of_directory_size" フォルダを作成することにします。

function (Measure-Directory) は以前のものを流用します。Gist からスクリプトをダウンロードして .psm1 のまま保存してください。今回は "get_the_list_of_directory_size.psm1" で保存することにします。
get_the_list_of_directory_size.psm1 · GitHub

function を呼び出すスクリプトは以下の通りです。.ps1 で保存してください。今回は "caller.ps1" で保存することにします。

$measureDirectoryModuleName = "get_the_list_of_directory_size.psm1"
$measureDirectoryModulePath = Join-Path $PSScriptRoot $measureDirectoryModuleName

Import-Module $measureDirectoryModulePath -Force

$csvFilePrefix = "DirectorySizes_"
$datetimeFormat = "yyyy-MM-dd_HH-mm-ss"
$csvExtension = ".csv"
$csvPath = Join-Path ([Environment]::GetFolderPath("MyDocuments")) "$($csvFilePrefix)$((Get-Date).ToString($datetimeFormat))$($csvExtension)"

foreach ($a in $args) {
    Get-ChildItem -LiteralPath $a -Recurse -Directory | Measure-Directory | Export-Csv -Path $csvPath -Encoding utf8 -Append -NoTypeInformation
}

Pause

最終的なフォルダ構成はこのようになります。

D:\Users\taidalog\Documents
└──get_the_list_of_directory_size
   ├─caller.ps1
   └─get_the_list_of_directory_size.psm1

ショートカットファイルを作る

caller.ps1 へのショートカットファイルを作成して、ファイル名を分かりやすいように変えてください。今回は「フォルダサイズの一覧を取得する」にします。
get_the_list_of_directory_size.psm1 へのショートカットファイルではありません。

次にプロパティを開いて「リンク先(T):」の先頭に以下の文字列を追加します。元々入っている文字列を消さないように注意してください。スクリプトをネットワーク上に置く場合は、RemoteSignedBypass に変更してください。

PowerShell -ExecutionPolicy RemoteSigned -NoExit -File

最終的にこうなります。

PowerShell -ExecutionPolicy RemoteSigned -NoExit -File D:\Users\taidalog\Documents\get_the_list_of_directory_size\caller.ps1

「適用(A)」をクリックすると PowerShell の部分が C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe に変わりますが、それが正常です。

同時にアイコンも変わるはずです。変わっていなかったら何か間違っている可能性があります。

-File が重要です。これが無くても動くには動きますが、パスに半角スペースや半角カッコが入っているとエラーになります。私はこれを抜かしていたために来る日も来る日もエラーを大量生産していました。

「送る(N)」に追加する

これで最後。いよいよ右クリックに登録します。

右クリックメニューへの登録を調べるとレジストリをいじる方法が出てきて諦めてしまう人もいると思います。実はもっと簡単な方法があります。

コンテキストメニューの「送る(N)」って使ったことありますか?

エクスプローラでファイルやフォルダを右クリックするとメニューが出ますよね。その中に「送る(N)」という項目があるはずです。

コンテキストメニューの「送る(N)」

たとえば、この中の「ドキュメント」を選択すると、右クリックしたファイルやフォルダをドキュメントフォルダに送ることができます。送るという言葉からファイルの移動(=切り取り+貼り付け)を想像しましたが、実際にはコピーを行うようですね。

ここにフォルダのショートカットファイルを追加すると、「送る(N)」からそのフォルダにコピーできるのですが、ここにスクリプトのショートカットファイルを追加すると、「送る(N)」からスクリプトを起動できるようになるのです。

というわけやってみましょう。Win + R で「ファイル名を指定して実行」を開き、shell:sendto と入力してOK してください。

「SendTo」というフォルダが開きましたね。ここに、先ほど作成したショートカットファイルを切り取り+貼り付けします。

さて、コンテキストメニューを開くと……

「送る(N)」にスクリプトを追加できた

いょっしゃぁぁ! できました!

知恵と意志を持つ人類が、管理者権限の助けなしにここまで来てるよ。

使い方

  1. エクスプローラーを開き、
  2. サイズを知りたいフォルダを選択し、
  3. 右クリック→送る(N)→フォルダサイズの一覧を取得する

です。

これでフォルダサイズの一覧が CSV ファイルになってドキュメントフォルダに出てきます。もちろん、フォルダを複数選択して右クリック→(以下略)すれば、まとめてサイズを取得できます。その分時間がかかりますけどね。

CSV ファイルから取り込んだ段階では、数字のデータであっても文字列になっています。これを数値に変換する方法は、:3.0の記事を参照してください。
PowerShell でフォルダサイズの一覧を取得する:3.0 REDO. - タイダログ

Measure-Directory の説明を書いてなかった

そもそも Measure-Directory の説明を書いていませんでした。この function は、

  1. 受け取ったフォルダの直下のファイルサイズを合計し、(=そのフォルダのサイズ)
  2. そのフォルダの Path その他諸々とまとめて PSCustomObject で出力する

ということをしています。

フォルダはパイプラインから受け取れます。Get-ChildItem -Recurse -Directory で渡してください

また、出力した PSCustomObject もパイプラインで次のコマンドレットに渡せます。Select なり Sort なり Where なり何でもどうぞ。

サブディレクトリも含めたサイズを取得する

フォルダAの中にフォルダBとフォルダCがあるとします。それぞれのフォルダ内にはファイルがあります。

A
├─B
└─C

上で説明した通り Measure-Directory では、Aのサイズとして取得できるのはA直下のファイルのサイズの合計です。BやCの中身は含んでいません。

ですがBやCを含んだ形でフォルダサイズを知りたいこともあるでしょう。ということで、サブディレクトリ対応型スクリプト8号機です(たぶんシリーズ中8個目くらいのスクリプト)。上記の caller.ps1 の代わりにこれを「送る(N)」に追加してください。

$measureDirectoryModuleName = "get_the_list_of_directory_size.psm1"
$measureDirectoryModulePath = Join-Path $PSScriptRoot $measureDirectoryModuleName

Import-Module $measureDirectoryModulePath -Force

$csvFilePrefix = "DirectorySizes_"
$datetimeFormat = "yyyy-MM-dd_HH-mm-ss"
$csvExtension = ".csv"
$csvPath = Join-Path ([Environment]::GetFolderPath("MyDocuments")) "$($csvFilePrefix)$((Get-Date).ToString($datetimeFormat))$($csvExtension)"

[long]$n = 0

foreach ($a in $args) {
    $directorySizes = Get-ChildItem -LiteralPath $a -Recurse -Directory | Measure-Directory

    foreach ($ds in $directorySizes) {
        [long]$lengthTotal = 0
        $subDirectoriesAndItself = @($directorySizes | Where-Object {$_.FullName -match "$([regex]::Escape($ds.FullName))(\\.+)?"})

        if ($subDirectoriesAndItself.Length -gt 0) {
            $lengthTotal = ($subDirectoriesAndItself | Measure-Object -Property Length -Sum).Sum
        }

        $ds | Select-Object `
            @{label="Length"; expression={$_.Length}},
            @{label="LengthMB"; expression={$_.LengthMB}},
            @{label="LengthGB"; expression={$_.LengthGB}},
            @{label="LengthTotal"; expression={$lengthTotal}},
            @{label="DirectoryCount"; expression={$subDirectoriesAndItself.Length}},
            @{label="FullName"; expression={$_.FullName}} | 
            Export-Csv -Path $csvPath -Encoding utf8 -Append -NoTypeInformation
        
        $n++
        
        $writeProgressParameters = @{
            Activity = "フォルダサイズの一覧を取得中……"
            Status = ([string]::Format("{0,8} 個目のフォルダを処理しています。", $n))
            Id = 0
            PercentComplete = -1
        }

        Write-Progress @writeProgressParameters
    }
}

Pause

それぞれのフォルダに対していちいちサブディレクトリを取得していては無駄が多くなります。ですので、一度、全てのフォルダにおいて、それ自身のフォルダサイズのみを取得した後、正規表現で自身とそのサブディレクトリを抽出してサイズを合計しています。

サブディレクトリは、「自身のパス + "\" + 何かしらの文字列」と考えればいいですね。正規表現では 自身のパス\\.+ と表せます。ただこれだと「自身のパス」だけのものが引っかかりません。自身のパス(\\.+)? とすることで、「自身のパス」と「自身のパス\何かしらの文字列」の両方を抽出することができます。

さようなら、全てのフォルダのプロパティを見る作業

私と Windows PowerShell の出会いのきっかけでもある「フォルダサイズの一覧を取得する」シリーズもこれで終わりです。

今回紹介したスクリプトGitHub に上げました。よろしければどうぞ。
taidalog / get_the_list_of_directory_size - GitHub

仕事の上でフォルダサイズを知りたいことはあっても、それが仕事の中心ということはあまりないと思います。このシリーズを読んでくださった方が、サクッとフォルダサイズの一覧を取得できて、本来のお仕事に時間をより多く割けたら、わたしも幸いです。

終劇

更新履歴