読者です 読者をやめる 読者になる 読者になる

tech.guitarrapc.cóm

C#, PowerShell, Unity, Cloud, Serverless Technical Update and Features

PowerShell で フォルダの容量一覧を取得したい

PowerShell

ドライブ容量は簡単ですね。 cim でも wmi でもいいでしょう。

さて、ドライブの次はフォルダ容量を調べてみましょう。 特に多くの場合は、 容量を調べる = どのファイル/フォルダが原因かを調べるため なので大事です。

では、見てみましょう。

フォルダの容量 = ファイルの容量合計

正確には違います。 さらに正確にいうと ACE/ACL (所有者設定/セキュリティ権限) や 隠しファイル などにより、取得には制限がかかる場合があるためです。

ただ、一般的にこれらのファイルは通常のファイルを調べたうえで更に問題と思わしきときに調べるのが筋でしょう。

よって、今回は実行ユーザーで見えるフォルダ内部のファイル容量を合計することにしましょう。*1

コードサンプル

例として、 c:\logs のフォルダ容量を見てみたいとするとこのような感じで書けます。

ワンライナー (定番)

# Oneliner
Get-ChildItem c:\logs -Recurse | where PSIsContainer | %{$i=$_;$subFolderItems = (Get-ChildItem $i.FullName | where Length | measure Length -sum);[PSCustomObject]@{Fullname=$i.FullName;MB=[decimal]("{0:N2}" -f ($subFolderItems.sum / 1MB))}} | sort MB -Descending | ft -AutoSize

このような感じで容量順に表示されます。

Fullname           MB
--------           --
C:\logs\past  1000.00
C:\logs\source0 10.00
C:\logs\sources  6.00
C:\logs\test     2.00

ワンライナー (改行付で)

少し見やすくしましょう。

# refine oneliner
Get-ChildItem c:\logs -Recurse `
        | where PSIsContainer `
        | %{
            $i=$_
            $subFolderItems = (Get-ChildItem $i.FullName | where Length | measure Length -sum)
            [PSCustomObject]@{
                Fullname=$i.FullName
                MB=[decimal]("{0:N2}" -f ($subFolderItems.sum / 1MB))
            }} `
        | sort MB -Descending `
        | format-Table -AutoSize

分離

ある程度以上の容量でないと、逆に実行速度遅くなります。

$folder = Get-ChildItem c:\logs -recurse | where PSIsContainer
[array]$volume = foreach ($i in $folder)
{
    $subFolderItems = (Get-ChildItem $i.FullName | where Length | measure Length -sum)
    [PSCustomObject]@{
        Fullname=$i.FullName
        MB=[decimal]("{0:N2}" -f ($subFolderItems.sum / 1MB))
    }
}
$volume | sort MB -Descending | ft -AutoSize

function化

使いやすくしましょう。

function Get-DirectoryVolume{

    [CmdletBinding()]
    param(
        [parameter(
            position = 0,
            mandatory,
            valuefrompipelinebypropertyname,
            valuefrompipeline)]
        [string]
        $path = $null,

        [parameter(
            position = 1,
            mandatory = 0,
            valuefrompipelinebypropertyname,
            valuefrompipeline)]
        [string]
        $scale = "MB"
    )

    Get-ChildItem $path -Recurse `
        | where PSIsContainer `
        | %{
            $i = $_
            $subFolderItems = (Get-ChildItem $i.FullName | where Length | measure Length -sum)
            [PSCustomObject]@{
                Fullname = $i.FullName
                MB = [decimal]("{0:N2}" -f ($subFolderItems.sum / "1$Scale"))
            }} `
        | sort $scale -Descending `
        | Format-Table -AutoSize
}

実行時にパスを指定できます。

Get-DirectoryVolume -path C:\Logs

イベント通知でもいいですし、フォルダ容量管理が遠隔できればあとは簡単ですね。

修正

意外とみなさんに需要があるようなので、修正しておきます。

function Get-DirectoryVolume
{

    [CmdletBinding()]
    param
    (
        [parameter(
            position = 0,
            mandatory = 1,
            valuefrompipeline = 1,
            valuefrompipelinebypropertyname = 1)]
        [string[]]
        $Path = $null,

        [parameter(
            position = 1,
            mandatory = 0,
            valuefrompipelinebypropertyname = 1)]
        [validateSet("KB", "MB", "GB")]
        [string]
        $Scale = "KB",

        [parameter(
            position = 2,
            mandatory = 0,
            valuefrompipelinebypropertyname = 1)]
        [switch]
        $Recurse = $false,

        [parameter(
            position = 3,
            mandatory = 0,
            valuefrompipelinebypropertyname = 1)]
        [switch]
        $Ascending = $false,

        [parameter(
            position = 4,
            mandatory = 0,
            valuefrompipelinebypropertyname = 1)]
        [switch]
        $OmitZero
    )

    process
    {
        $path `
        | %{
            Write-Verbose ("Checking path : {0}. Scale : {1}. Recurse switch : {2}. Decending : {3}" -f $_, $Scale, $Recurse, !$Ascending)
            if (Test-Path $_)
            {
                $result = Get-ChildItem -Path $_ -Recurse:$Recurse `
                | where PSIsContainer `
                | %{
                    $subFolderItems = (Get-ChildItem $_.FullName | where Length | measure Length -sum)
                    [PSCustomObject]@{
                        Fullname = $_.FullName
                        $scale = [decimal]("{0:N4}" -f ($subFolderItems.sum / "1{0}" -f $scale))
                    }} `
                | sort $scale -Descending:(!$Ascending)

                if ($OmitZero)
                {
                    return $result | where $Scale -ne ([decimal]("{0:N4}" -f "0.0000"))
                }
                else
                {
                    return $result
                }
            }
        }
    }
}

これで、以下が可能になりました。

  • Pipeline入力を受け付け
  • 複数のパス対応
  • Scaleの選択
  • Recurseの選択
  • 0出力の省略
Get-DirectoryVolume -path D:\GitHub, c:\logs -scale KB
Get-DirectoryVolume -path D:\GitHub, c:\logs -scale KB
Get-DirectoryVolume -path D:\GitHub, c:\logs -scale KB -Ascending
Get-DirectoryVolume -path D:\GitHub, c:\logs -scale KB -Ascending -OmitZero
"c:\logs", "D:\GitHub" | Get-DirectoryVolume  -scale KB
"c:\logs", "D:\GitHub" | Get-DirectoryVolume  -scale MB -Recurse -Verbose

GitHub

コードを公開しておきます。

GitHub - PowerShellUtil / Get-DirecotryVolume

*1:このことから、Administrator で実行すると Windows/Program Files でも動作するのでいいかもしれません。