public/Get-PSTree.ps1

using namespace System.IO
using namespace System.Collections.Generic

<#
.SYNOPSIS
tree like function for PowerShell
 
.DESCRIPTION
PowerShell function that intends to emulate the tree command with added functionality to calculate the folders size as well as recursive folders size.
 
.PARAMETER Path
Absolute or relative folder path.
 
.PARAMETER Depth
Controls the recursion limit
 
.PARAMETER Recurse
Traverse all Directory hierarchy
 
.PARAMETER Force
Displays hidden Files and Folders
 
.PARAMETER Directory
Displays Folders only
 
.PARAMETER RecursiveSize
Displays the recursive Folders Size
 
.INPUTS
System.String
 
You can pipe a string that contains a path to Get-PSTree.
Can take pipeline input from cmdlets that outputs System.IO.DirectoryInfo.
 
.OUTPUTS
Object[], PSTreeDirectory, PSTreeFile
 
.EXAMPLE
Get-PSTree
 
Get hierarchy of the current Directory with default parameters (`-Depth 3`)
 
.EXAMPLE
Get-PSTree -Directory -Recurse
 
Get hierarchy of the current Directory recursively displaying only Folders
 
.EXAMPLE
Get-PSTree -Depth 2 -Force
 
Get hierarchy of the current Directory 2 levels deep and displaying hidden Folders
 
.EXAMPLE
Get-PSTree -Depth 2 -RecursiveSize -Directory
 
Get hierarchy 2 levels deep displaying only Folders with their recursive size
 
.LINK
https://github.com/santisq/PSTree
#>


function Get-PSTree {
    [cmdletbinding(DefaultParameterSetName = 'Depth')]
    param(
        [parameter(Position = 0, ValueFromPipeline)]
        [alias('FullName')]
        [ValidateScript({
            if(Test-Path $_ -PathType Container) {
                return $true
            }
            throw 'Invalid Folder Path.'
        })]
        [string] $Path = $PWD,

        [ValidateRange(1, [int]::MaxValue)]
        [parameter(ParameterSetName = 'Depth', Position = 1)]
        [int] $Depth = 3,

        [parameter(ParameterSetName = 'Recurse', Position = 1)]
        [switch] $Recurse,

        [parameter()]
        [switch] $Force,

        [parameter()]
        [switch] $Directory,

        [parameter()]
        [switch] $RecursiveSize
    )

    begin {
        $withDepth = $PSBoundParameters.ContainsKey('Depth')
        $hidden    = [FileAttributes] 'Hidden'
        $ignore    = [HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
        $indexer   = @{}
    }

    process {
        $Path = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path).TrimEnd('\/')
        if($Path.EndsWith(':')) {
            $Path = $ExecutionContext.SessionState.Path.Combine($Path, '')
        }

        $stack = [Stack[PSTreeDirectory]]::new()
        $stack.Push([PSTreeDirectory]::new($Path, 0))

        $output = :outer while($stack.Count) {
            $next  = $stack.Pop()
            $level = $next.Depth + 1
            $size  = 0

            if(-not $RecursiveSize.IsPresent -and $withDepth -and $next.Depth -gt $Depth) {
                continue
            }

            try {
                $enum = $next.EnumerateFileSystemInfos()
            }
            catch {
                $PSCmdlet.WriteError($_)
                continue
            }

            $files = foreach($item in $enum) {
                if($item -is [FileInfo]) {
                    $size += $item.Length

                    if(-not $Force.IsPresent -and $item.Attributes.HasFlag($hidden)) {
                        continue
                    }

                    if(-not $RecursiveSize.IsPresent -and $withDepth -and $level -gt $Depth) {
                        continue
                    }

                    [PSTreeFile]::new($item, $level)
                    continue
                }

                $stack.Push([PSTreeDirectory]::new($item, $level))
            }

            $next.SetSize($size)
            $absolutePath = $next.GetAbsolutePath()
            $indexer[$absolutePath] = $next
            $parents = $next.GetParents($indexer)

            if($RecursiveSize.IsPresent) {
                foreach($parent in $parents) {
                    $indexer[$parent].AddSize($size)
                }
            }

            if($absolutePath -ne $Path) {
                if(-not $Force.IsPresent -and $next.GetAttributes().HasFlag($hidden)) {
                    $null = $ignore.Add($absolutePath)
                    continue
                }
            }

            foreach($parent in $parents) {
                if($ignore.Contains($parent)) {
                    continue outer
                }
            }

            if($withDepth -and $next.Depth -gt $Depth) {
                continue
            }

            $next

            if(-not $Directory.IsPresent) {
                $files
            }
        }

        [PSTreeStatic]::DrawHierarchy($output, 'Hierarchy', 'Depth')
    }
}