<# .SYNOPSIS Gets the logical size of directories in bytes. .DESCRIPTION Given a literal directory path, output that directory's logical size, i.e., the sum of all files contained in the directory, including hidden ones. NOTE: * The logical size is distinct from the size on disk, given that files are stored in fixed-size blocks. Furthermore, files can be compressed or sparse. Thus, the size of regular files on disk is typically greater than their logical size; conversely, compressed and sparse files require less disk space. Finally, the list of child items maintained by the filesystem for each directory requires disk space too. * Wildcard expressions aren't directly supported, but you may pipe in Output from Get-ChildItem / Get-Item; if files rather than directotries happen to be among the input objects, their size is reported as-is. CAVEATS: * Can take a long time to run with large directory trees, especially with -Recurse. * Recursion is implemented inefficently. .PARAMETER LiteralPath The literal path of a directory. May be provided via the pipeline. .PARAMETER Recurse Calculates the logical size not only of the input directory itself, but of all subdirectories in its subtree too. To limit the recursion depth, use -Depth. .PARAMETER Depth Limits the recursion depth to the specified number of levels. Implies -Recurse. Note that 0 means no recursion. Use just -Recurse in order not to limit the recursion. .PARAMETER ExcludeSelf Excludes the target directory itself from the size calculation. Implies -Recurse. Since -Depth implies -Recurse, you could use -ExcludeSelf -Depth 1 to report only the sizes of the immediate subdirectories. .OUTPUTS [pscustomobject] instances with properties FullName, Size, and FriendlySize. .EXAMPLE Get-DirectorySize Gets the logical size of the current directory. .EXAMPLE Get-DirectorySize -Recurse Gets the logical size of the current directory and all its subdirectories. .EXAMPLE Get-DirectorySize /path/to -ExcludeSelf -Depth 1 | Sort-Object Size Gets the logical size of all child directories in /path/to without including /path/to itself, and sorts the result by size (largest last). #> function Get-DirectorySize { param( [Parameter(ValueFromPipeline)] [Alias('PSPath')] [string] $LiteralPath = '.', [switch] $Recurse, [switch] $ExcludeSelf, [int] $Depth = -1, [int] $__ThisDepth = 0 # internal use only ) process { # Resolve to a full filesystem path, if necessary $fullName = if ($__ThisDepth) { $LiteralPath } else { Convert-Path -ErrorAction Stop -LiteralPath $LiteralPath } if ($ExcludeSelf) { # Exclude the input dir. itself; implies -Recurse $Recurse = $True $ExcludeSelf = $False } else { # Process this dir. # Calculate this dir's total logical size. # Note: [System.IO.DirectoryInfo].EnumerateFiles() would be faster, # but cannot handle inaccessible directories. $size = [Linq.Enumerable]::Sum( [long[]] (Get-ChildItem -Force -Recurse -File -LiteralPath $fullName).ForEach('Length') ) # Create a friendly representation of the size. $decimalPlaces = 2 $padWidth = 8 $scaledSize = switch ([double] $size) { {$_ -ge 1tb } { $_ / 1tb; $suffix='tb'; break } {$_ -ge 1gb } { $_ / 1gb; $suffix='gb'; break } {$_ -ge 1mb } { $_ / 1mb; $suffix='mb'; break } {$_ -ge 1kb } { $_ / 1kb; $suffix='kb'; break } default { $_; $suffix='b'; $decimalPlaces = 0; break } } # Construct and output an object representing the dir. at hand. [pscustomobject] @{ FullName = $fullName FriendlySize = ("{0:N${decimalPlaces}}${suffix}" -f $scaledSize).PadLeft($padWidth, ' ') Size = $size } } # Recurse, if requested. if ($Recurse -or $Depth -ge 1) { if ($Depth -lt 0 -or (++$__ThisDepth) -le $Depth) { # Note: This top-down recursion is inefficient, because any given directory's # subtree is processed in full. Get-ChildItem -Force -Directory -LiteralPath $fullName | ForEach-Object { Get-DirectorySize -LiteralPath $_.FullName -Recurse -Depth $Depth -__ThisDepth $__ThisDepth } } } } }