Skip to content

Instantly share code, notes, and snippets.

@XPlantefeve
Last active May 31, 2021 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save XPlantefeve/e1de3e1eaae5b0f8c6c5d409dfb4a946 to your computer and use it in GitHub Desktop.
Save XPlantefeve/e1de3e1eaae5b0f8c6c5d409dfb4a946 to your computer and use it in GitHub Desktop.
function Get-HumanReadableSize {
<#
.SYNOPSIS
Outputs a human readable version of a file size
.DESCRIPTION
This function rounds the value of the given file size to the nearest relevant
multiplier prefix (Gb, Mb, or Kb)
.INPUTS
long
.OUTPUTS
string
#>
param(
[Parameter(Mandatory, ValueFromPipeline)]
[long]$InputObject,
[double]$Precision = 0.9
)
process {
if (($result = $_ / 1Gb) -gt $Precision) {
'{0:n2}G' -f $result
} elseif (($result = $_ / 1Mb) -gt $Precision) {
'{0:n2}M' -f $result
} elseif (($result = $_ / 1Kb) -gt $Precision) {
'{0:n2}K' -f $result
} else {
$_
}
}
}
function Get-FolderSize {
<#
.SYNOPSIS
Gets information of subfolders content
.DESCRIPTION
The Get-SubfolderSize function will test every subfolder of the provided path for size,
number of folders, and number of files.
.PARAMETER Path
The folder whose subfolders should be measured
.EXAMPLE
Get-SubfolderSize -Path C:\Localdata
Gets the size of every immediate subfolder of C:\Localdata
.NOTES
This function uses Robocopy internally, as Robocopy is build for speed whereas
Powershell needs to construct the whole .NET object for every folder and file
and is way slower.
Reparse points (IE. hard links) are excluded to avoid loops.
#>
param(
[Parameter(Mandatory)]
[string]$Path
)
function Get-ResultFromRobocopy {
param(
[string]$Name,
[string[]]$robocopyInfo
)
$properties = @{Name = $Name }
foreach ($line in $robocopyInfo) {
if ($line -match '(?<infoname>Dirs|Files|Bytes) :\s+(?<infovalue>\d+)') {
$properties[$Matches.infoname] = $Matches.infovalue
}
}
$output = [PSCustomObject]$properties |
Select-Object -Property @(
'Name'
@{l = 'Size'; e = { $_.Bytes | Get-HumanReadableSize } }
'Bytes'
'Dirs'
'Files'
)
# We build a default display property set so as not to clutter the output.
# The Byte property is hidden but still available if needed.
$DefaultDisplayPropertySet = 'DefaultDisplayPropertySet', [string[]]('Name', 'Size', 'Dirs', 'Files')
$PSMemberInfo = New-Object -TypeName Management.Automation.PSPropertySet -ArgumentList $DefaultDisplayPropertySet
$PSStandardMembers = [Management.Automation.PSMemberInfo[]]$PSMemberInfo
$output | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers -PassThru
}
$robocopyParameters = @(
'C:\DUMMY' # Non-existant destination folder
'/l' # List only (blank run): we only pretend to copy to get the size information
'/bytes' # Robocopy rounds the size information. We need this to get an accurate number
'/np','/nfl','/ndl','/njh' # We remove all output but the footer
'/r:0','/w:0' # There's no point in retrying a failed copy, it's not a network problem
'/xjf','/xjd' # We skip over junctions (hard links) to avoid loops.
)
$i = 0
$subfolders = Get-ChildItem -Path $Path -Directory -Force | ? Attributes -NotMatch ReparsePoint
$percenttotal = (1+$subfolders.count)/100
$activity = 'Analyzing {0} folders' -f (1+$subfolders.count)
# We start with a pecial case: the folder itself.
Write-Progress -Activity $activity -PercentComplete ($i++/$percenttotal) -CurrentOperation 'root folder'
$robocopyInfo = Robocopy.exe $Path $robocopyParameters
Get-ResultFromRobocopy -Name . -robocopyInfo $robocopyInfo
# We only read one level of files for the root folder, but we need the whole tree for every
# subfolder, so we add the /mir parameter for recursive processing
$robocopyParameters += '/mir'
foreach ($folder in $subfolders) {
Write-Progress -Activity $activity -PercentComplete ($i++/$percenttotal) -CurrentOperation $folder.Name
$robocopyInfo = Robocopy.exe $folder.FullName $robocopyParameters
Get-ResultFromRobocopy -Name $folder.Name -robocopyInfo $robocopyInfo
}
}
function Get-FolderSizeReport {
<#
.SYNOPSIS
Outputs a report of subfolders content
.DESCRIPTION
The Get-SubfolderReport function will output a table information on every
subfolder of the provided path for size, and percentage of the size of the
whole folder.
.PARAMETER Path
The folder whose subfolders should be measured
.EXAMPLE
Get-SubfolderReport -Path C:\Localdata
Gives information about C:\Localdata subfolders
#>
param(
[Parameter(Mandatory)]
[string]$Path
)
$table = Get-FolderSize -Path $Path
$sum = $table | Measure-Object -Sum -Property bytes | Select-Object -ExpandProperty Sum
$table | Select-Object -Property @(
'Name'
'Size'
@{l = 'Percent'; e = { [math]::Round((($_.Bytes) / $sum * 100), 2) } }
) | Sort-Object -Descending -Property percent
# We build a footer
[PSCustomObject]@{
Name = '----'
Size = '----'
Percent = '-------'
}
[PSCustomObject]@{
Name = 'Total'
Size = $sum | Get-HumanReadableSize
Percent = 100
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment