Last year I posted a PowerShell function to measure the size of a folder. I recently had a need to use it again, and realized it needed a few tweaks. By default, the original version recursively searched through all subfolders. But there may be situations where you only want to measure the top level folder, so I added a –NoRecurse parameter. I also added a parameter to display an average value as well.
Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!

Figure 1 Measuring the Temp folder in GB

Figure 2 Measuring my Documents folder including average size in KB
Here is the updated function:
Function Measure-Folder {
<#
.SYNOPSIS
Measure the size of a folder.
.DESCRIPTION
This command will take a file path and create a custom measurement object that shows the number of files and the total size. The default size will be in bytes but you can specify a different unit of measurement. The command will format the result accordingly and dynamically change the property name as well.
.PARAMETER Path
The default is the current path. The command will fail if it is not a FileSystem path.
.Parameter Average
Calculate the average file size. This will be formatted with the same unit as the total size.
.PARAMETER NoRecurse
The default behavior is to recurse through all subfolders. But you can suppress that by using -NoRecurse.
.PARAMETER Unit
The default unit of measurement is bytes, but you can use any of the standard PowerShell numeric shortcuts: "KB","MB","GB","TB","PB"
.EXAMPLE
PS C:\> measure-folder c:\scripts
Path Name Count Size
---- ---- ----- ----
C:\scripts scripts 2858 43800390
Measure the scripts folder using the default size of bytes.
.EXAMPLE
PS C:\> dir c:\scripts -Directory | measure-folder -Unit kb | Sort Size* -Descending | Select -first 5 | format-table -AutoSize
Path Name Count SizeKB
---- ---- ----- ------
C:\scripts\GP GP 40 2287.080078125
C:\scripts\Workflow Workflow 64 1253.0185546875
C:\scripts\modhelp modhelp 1 386.4970703125
C:\scripts\stuff stuff 4 309.09375
C:\scripts\ADTFM-Scripts ADTFM-Scripts 76 297.7880859375
Get all the child folders under C:\scripts, measuring the size in KB. Sort the results on the size property in descending order. Then select the first 5 objects and format the results as a table.
.EXAMPLE
PS C:\> measure-folder $env:temp -Average -unit MB
Path : C:\Users\Jeff\AppData\Local\Temp
Name : Temp
Count : 64626
SizeMB : 6769.94603252411
AvgMB : 0.104755764437287
Measure all the %TEMP% folder, including a file average all formatted in MB.
.NOTES
Last Updated: July 13, 2015
Version : 2.0
Originally published at https://jdhitsolutions.com/blog/scripting/3715/friday-fun-the-measure-of-a-folder/
Learn more about PowerShell:
Essential PowerShell Learning Resources
****************************************************************
* DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
* THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF *
* YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
* DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. *
****************************************************************
.LINK
Get-ChildItem
Measure-Object
.INPUTS
string or directory
.OUTPUTS
Custom object
#>
[cmdletbinding()]
Param(
[Parameter(Position=0,ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[ValidateScript({
if (Test-Path $_) {
$True
}
else {
Throw "Cannot validate path $_"
}
})]
[Alias("fullname")]
[string]$Path = ".",
[switch]$NoRecurse,
[Alias("avg")]
[switch]$Average,
[ValidateSet("bytes","KB","MB","GB","TB","PB")]
[string]$Unit = "bytes"
)
Begin {
Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"
#hash table of parameters to Splat to Get-ChildItem
$dirHash = @{
File = $True
Recurse = $True
}
if ($NoRecurse) {
Write-Verbose "No recurse"
$dirHash.remove("recurse")
}
#hash table of parameters to splat to measure-Object
$measureHash = @{
Property = "length"
Sum = $True
}
if ($Average) {
Write-Verbose "Including Average"
$measureHash.Add("Average",$True)
}
} #begin
Process {
$Resolved = Resolve-Path -Path $path
$Name = Split-Path -Path $Resolved -Leaf
#verify we are in the file system
if ($Resolved.Provider.Name -eq 'FileSystem') {
#define a hash table to hold new object properties
$propHash = [ordered]@{
Path=$Resolved.Path
Name=$Name
}
Write-Verbose "Measuring $resolved in $unit"
$dirHash.Path = $Resolved
$stats = Get-ChildItem @dirHash | Measure-Object @measureHash
Write-Verbose "Measured $($stats.count) files"
$propHash.Add("Count",$stats.count)
Switch ($Unit) {
"bytes" {
$propHash.Add("Size",$stats.sum)
if ($average) {
$propHash.Add("Avg",$stats.Average)
}
break
}
"kb" {
$propHash.Add("SizeKB",$stats.sum/1KB)
if ($average) {
$propHash.Add("AvgKB",$stats.Average/1KB)
}
break
}
"mb" {
$propHash.Add("SizeMB",$stats.sum/1MB)
if ($average) {
$propHash.Add("AvgMB",$stats.Average/1MB)
}
break
}
"gb" {
$propHash.Add("SizeGB",$stats.sum/1GB)
if ($average) {
$propHash.Add("AvgGB",$stats.Average/1GB)
}
break
}
"tb" {
$propHash.Add("SizeTB",$stats.sum/1TB)
if ($average) {
$propHash.Add("AvgTB",$stats.Average/1TB)
}
break
}
"pb" {
$propHash.Add("SizePB",$stats.sum/1PB)
if ($average) {
$propHash.Add("AvgPB",$stats.Average/1PB)
}
break
}
} #switch
#write the new object to the pipeline
New-Object -TypeName PSobject -Property $propHash
}
else {
Write-Warning "You must specify a file system path."
}
} #process
End {
Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
} #end
} #end function
I hope you'll let me know what you think or what additional features would be helpful.
Update: For an even newer version of this function go here.

What do you think about creating a $Units hashtable to eliminate the switch statement? Seems like a good opportunity to me because the switch branches are very similar.
That is a terrific alternative.
In place of the Switch statement, you could use code like this:
$unitHash = @{
Bytes = 1
KB = 1KB
MB = 1MB
GB = 1GB
TB = 1TB
PB = 1PB
}
$value = $stats.sum/$UnitHash.item($unit)
$Label = "Size"
if ($unit -ne 'bytes') {
$label+= $($unit.ToUpper())
}
$propHash.Add($label,$value)
#repeat process for Average
if ($Average) {
$value = $stats.average/$UnitHash.item($unit)
$Label = "Avg"
if ($unit -ne 'bytes') {
$label+= $($unit.ToUpper())
}
$propHash.Add($label,$value)
} #if Average
Thanks for sharing the code to Measure-Folder
One missing thing is help text for the -Average switch.
I went ahead and added a little something.
Jeffery, I like the simplicity of this. What would be an easy method to round off (to the thousandths place, for example) instead of having “6.611275422…GB” in Figure 1?
Thank you,
Manfred
I find it easiest to use the Round() method from the [MATH] class. Here’s a sample that would round to 2 decimal points.
$propHash.Add(“SizeTB”,[math]::round($stats.sum/1TB,2))
if ($average) {
$propHash.Add(“AvgTB”,[math]::Round($stats.Average/1TB,2))
Perhaps I should add a rounding parameter. Or you can!
}