Today I went in search of a PowerShell script to calculate the size of a directory. I was amazed to find very little. Even worse, I was disappointed in what I found. Here’s an example:
http://iformattable.blogspot.com/2007/07/powershell-script-to-determine.html
Cleaned up a little it looks like:
function Get-DirectorySize
{
param ($path = {(Get-Location).Path})
$size = Get-ChildItem $path -recurse -force `
| ? { $_.GetType() -like 'System.IO.DirectoryInfo'} `
| % {$_.GetFiles() } `
| Measure-Object -Property Length -Sum `
| Measure-Object -Property Sum -Sum
$size2 = (Get-item $path).GetFiles() `
| Measure-Object -Property Length -Sum
[System.Math]::Round(($size.Sum + $size2.Sum) / (1024 * 1024)).ToString() + "MB"
}
Returning a String is ridiculous. If you are going to all the trouble of using Measure-Object you might as well get a rich output.
function Get-DirectorySize
{
param ($path = {(Get-Location).Path})
Get-ChildItem $path -recurse -force `
| ? { $_.GetType() -like 'System.IO.FileInfo'} `
| Measure-Object -Property Length -Sum -Average -Minimum -Maximum`
}
This is cleaner and has more interesting output providing an aggregated object with Sum, Average, Minimum and Maximum properties. However, the biggest problem is that is extremely SLOW. Try to use this on a directory with thousands of small files. Even worse, try to use it on a network path with thousands of small files!
This approach tries to leverage all the cool PowerShell piping and CmdLets. I prefer these to the old school VBScript and COM object approaches. However, sometimes old school beats new school.
function Get-DirectorySize
{
param ($path = {(Get-Location).Path})
if (! $GLOBAL:_FileSystemObject)
{
$GLOBAL:_FileSystemObject = New-Object -comobject "Scripting.FileSystemObject"
}
($GLOBAL:_FileSystemObject.GetFolder($path)).Size
}
The old FileSystemObject is hard to beat for this kind of job. In this example, I assure I don’t have to make more than one FileSystemObject.
function Get-DirectorySize
{
begin
{
if (! $GLOBAL:_FileSystemObject)
{
$GLOBAL:_FileSystemObject = New-Object -comobject "Scripting.FileSystemObject"
}
$accumulation = 0
}
process
{
if ($_ -eq $null)
{
$_ = (Get-Location).Path
}
if ($_ -is [System.IO.DirectoryInfo])
{
$_ = $_.FullName
}
$accumulation += ($GLOBAL:_FileSystemObject.GetFolder($_)).Size
}
end
{
$accumulation
}
}
This final example is Pipeline friendly and marshals a DirectoryInfo parameter. This function is very flexible and should be as fast as you are going to get.
NOTE: $accumulation accumulates multiple directories in the pipeline, not individual file sizes.
Related Post: Analyze Hard Drive Extensions with PowerShell