Finding Disk Hogs (i.e. Large Folders)
“Disk full? It was only at 60% last week! Where did all the space go?”
If you haven’t said this, then you probably won’t appreciate this post. It seems such a relatively simple task – give me a list of the biggest folders on the drive. Yes, C:\Windows (or wherever you’ve stashed $env:WinDir to) will be one of them, but we’ll then have to rely on eyeballing and common sense (uh-oh) to address the offenders once we’ve found our disk hogs.
In other words, if you do something like this: Get-FolderSize c:\ | Sort-Object -Descending -Property Size | Select-Object -First 10 | Remove Item
, you deserve what you get.
Humour aside, I run into this situation at least once a week. On my home network, I run WinDirStat, but at work we have certain ‘clean’ labs that don’t allow willy-nilly downloaded apps to run. In those labs, when I get the SCOM alert, I usually go searching for some script to do it. For some reason, I’ve yet to find one that works reliably. (It turns out I wrote one a few days ago, too. Oh, well.)
So I decided to write my own:
function Get-FolderSize
{param ([parameter(valuefrompipeline=$true)][string[]]$Path = @((Get-Location).ProviderPath));
begin
{
########################################
function invoke-worker
{
param ([string]$Path = $null);
# handle bum data
if (!$Path -or !(Test-Path -Path $Path))
{
return;
} # if (!$Path -or !(Test-Path -Path $Path))
$Path = (Resolve-Path -Path $Path).ProviderPath;
Write-Progress $MyInvocation.MyCommand.Name $Path;
[long]$size = 0;
# this is were most of the work occurs
Get-ChildItem -Path $Path |
% {
if ($_.PsIsContainer)
{ # if it's a folder, recurse and return size
$size += invoke-worker -Path $_.FullName;
} # if ($_.PsIsContainer)
else
{ # otherwise, add it's size
$size += $_.Length;
} # if ($_.PsIsContainer) ... else
} # Get-ChildItem -Path $Path |
# we've iterated (and recursed) through the folder. cache for summary view in the end {} block
$script:output += New-Object -TypeName PsObject |
Select-Object -Property @{
n = 'Path';
e = { $Path; }
}, @{
n = 'Size';
e = { $size; }
} # $object = New-Object -TypeName PsObject | Select-Object...
# output folder size
$size;
} # function invoke-worker
########################################
$script:output = @();
} # begin
process
{
foreach ($_path in $Path)
{ # this will return the file size, but we don't care. We want the summary view in the end {} block
invoke-worker -Path $Path | Out-Null;
} # foreach ($_path in $Path)
} # process
end
{ # filter out all the zero-length files
$script:output |
? { $_.size; }
} # end
} # function Get-FolderSize