du in PowerShell?

Viewed 44188

How can I get a du-ish analysis using PowerShell? I'd like to periodically check the size of directories on my disk.

The following gives me the size of each file in the current directory:

foreach ($o in gci)
{
   Write-output $o.Length
}

But what I really want is the aggregate size of all files in the directory, including subdirectories. Also I'd like to be able to sort it by size, optionally.

6 Answers

There is an implementation available at the "Exploring Beautiful Languages" blog:

"An implementation of 'du -s *' in Powershell"

function directory-summary($dir=".") { 
  get-childitem $dir | 
    % { $f = $_ ; 
        get-childitem -r $_.FullName | 
           measure-object -property length -sum | 
             select @{Name="Name";Expression={$f}},Sum}
}

(Code by the blog owner: Luis Diego Fallas)

Output:

PS C:\Python25> directory-summary

Name                  Sum
----                  ---
DLLs              4794012
Doc               4160038
include            382592
Lib              13752327
libs               948600
tcl               3248808
Tools              547784
LICENSE.txt         13817
NEWS.txt            88573
python.exe          24064
pythonw.exe         24576
README.txt          56691
w9xpopen.exe         4608

If you only need the total size of that path, one simplified version can be,

Get-ChildItem -Recurse ${HERE_YOUR_PATH} | Measure-Object -Sum Length

Building on previous answers, this will work for those that want to show sizes in KB, MB, GB, etc., and still be able to sort by size. To change units, just change "MB" to desired units in both "Name=" and "Expression=". You can also change the number of decimal places to show (rounding), by changing the "2".

function du($path=".") {
    Get-ChildItem $path |
    ForEach-Object {
        $file = $_
        Get-ChildItem -File -Recurse $_.FullName | Measure-Object -Property length -Sum |
        Select-Object -Property @{Name="Name";Expression={$file}},
                                @{Name="Size(MB)";Expression={[math]::round(($_.Sum / 1MB),2)}} # round 2 decimal places
    }
}

This gives the size as a number not a string (as seen in another answer), therefore one can sort by size. For example:

PS C:\Users\merce> du | Sort-Object -Property "Size(MB)" -Descending

Name      Size(MB)
----      --------
OneDrive  30944.04
Downloads    401.7
Desktop     335.07
.vscode     301.02
Intel         6.62
Pictures      6.36
Music         0.06
Favorites     0.02
.ssh          0.01
Searches         0
Links            0

My own take using the previous answers:

function Format-FileSize([int64] $size) {
    if ($size -lt 1024)
    {
        return $size
    }
    if ($size -lt 1Mb)
    {
        return "{0:0.0} Kb" -f ($size/1Kb)
    }
    if ($size -lt 1Gb)
    {
        return "{0:0.0} Mb" -f ($size/1Mb)
    }
    return "{0:0.0} Gb" -f ($size/1Gb)
}

function du {
        param(
        [System.String]
        $Path=".",
        [switch]
        $SortBySize,
        [switch]
        $Summary
    )
    $path = (get-item ".").FullName
    $groupedList = Get-ChildItem -Recurse -File $Path | 
        Group-Object directoryName | 
            select name,@{name='length'; expression={($_.group | Measure-Object -sum length).sum } }
    $results = ($groupedList | % {
        $dn = $_
        if ($summary -and ($path -ne $dn.name)) {
            return
        }
        $size = ($groupedList | where { $_.name -like "$($dn.name)*" } | Measure-Object -Sum length).sum
        New-Object psobject -Property @{ 
            Directory=$dn.name; 
            Size=Format-FileSize($size);
            Bytes=$size` 
        }
    })
    if ($SortBySize)
        { $results = $results | sort-object -property Bytes }
    $results | more
}
Related