TreeSize.psm1
# PowerShell 5 feature (oops, has to be the "first thing" in a script) # In PowerShell 4 we have to use Import-Namespace from the reflection module using namespace System.IO ## This is not working ... I guess we need a format file to show it right ## The objects appear to be typed correctly ( Get-Member shows them as "System.IO.TreeView" ) # Update-TypeData -TypeName "System.IO.TreeView" -DefaultDisplayProperty TreeName -DefaultDisplayPropertySet TreeName, Length -Force function Get-TreeSize { #.Synopsis # Recursively lists provider items and sums their lengths #.Description # The actual objects are the same file system objects you'd get from Get-ChildItem -Recurse # But with properties added to them like Depth and with Length calculated for the folders # Then a custom formmatter makes them come out like this: # # PS> Get-TreeSize # # Localization\ 12021 # ├─ En\ 1339 # ├─ En-US\ 2025 # # Or like this: # # PS> Get-TreeSize -ShowFiles # # Localization\ 12021 # ├─ En\ 1339 # ├─ numbers.psd1 86 # ├─ UserSettings.psd1 1253 # ├─ En-US\ 2025 # ├─ Localization.psd1 958 # ├─ numbers.psd1 67 # ├─ UserSettings.psd1 1000 # ├─ Localization.psd1 6698 # ├─ UserSettings.psd1 1959 # # Or like this: # # PS> Get-TreeSize | Format-Custom # # Localization\ (11.74 KB) # ├─ En\ (1.31 KB) # ├─ En-US\ (1.98 KB) [CmdletBinding()] param( # The root of the tree view [Parameter(ValueFromPipelineByPropertyName)] [Alias("PSPath")] $Path = $pwd, # Whether to show files or just folder in the resulting tree [Parameter()] [Switch]$ShowFiles, # How deep to start the indent (hypothetically for splitting up the work) # Defaults to the number of elements in $Path.Split("\") [int]$Depth = $((Convert-Path $Path).Split([Path]::DirectorySeparatorChar).Length) ) process { # I'm going to choose to show FileSystem paths here # Which means any PowerShell "PSDrive" will be lost # We could change that later ... $Local:Path = Convert-Path $Path $Local:Length = 0 # Cache the recursive output so it comes out in the right order $Children = @( switch(Get-ChildItem -Path $Local:Path -Force) { {$_ -is [DirectoryInfo]} { # Recurse! And don't forget to pass all the parameters down ... $Info = Get-TreeSize -Path $_.FullName -ShowFiles:$ShowFiles -Depth ($Depth + 1) # Running total ... # ROFL: this is the length of the array. stupid thing. $Local:Length += $Info[0].Length $Info } {$_ -is [FileInfo]} { # Running total ... $Local:Length += $_.Length if($ShowFiles) { $_ } } } ) @( # After we recurse, we know the size of this object, and can output. Get-Item -Path $Local:Path | Add-Member NoteProperty Length $Local:Length -Force -Passthru # Now output the child items ... $Children ) | ForEach-Object { $_.PSTypeNames.Insert(0, "System.IO.TreeView"); $_ } | Add-Member ScriptProperty Depth { $this.FullName.Split([Path]::DirectorySeparatorChar).Length } -Passthru -Force | # NOTE: Even though this is a ScriptProperty # We have to add it here, dynamically, instead of in a types file because we use $Depth Add-Member ScriptProperty "TreeName" { (" " * [Math]::Max(($this.Depth - $Depth -1), 0)) + $(if($this.Depth -ne $Depth){"├─ "}) + $this.Name + $(if($this.PSIsContainer){"\"})} -Passthru -Force } } Set-Alias TreeSize Get-TreeSize |