functions/process-tree.ps1
|
function Get-ProcessTree { [cmdletbinding()] [OutputType('PSProcessTree', 'PSProcessTree#User')] [alias('gpt')] param( [Parameter( Mandatory, Position = 0, ValueFromPipelineByPropertyName, HelpMessage = 'Specify a process ID' )] [ValidateNotNullOrEmpty()] [Alias('ProcessID', 'PID')] [int]$ID, [Parameter(HelpMessage = 'Include the process owner.')] [switch]$IncludeUserName ) begin { #tags are used for categorizing the command #cmdTags = general Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" if ($MyInvocation.CommandOrigin -eq 'Runspace') { #Hide this when the command is called from another command Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" } $data = [System.Collections.Generic.List[System.Diagnostics.Process]]::new() <# Adding a custom type name to the process object to allow for additional custom formatting #> if ($IncludeUserName) { $psName = 'PSProcessTree#User' } else { $psName = 'PSProcessTree' } } #begin process { if ($PSEdition -eq 'Desktop') { Write-Warning "This command requires PowerShell 7" Return } Write-Information $PSBoundParameters do { Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Getting process ID $ID" try { $r = Get-Process -Id $id -IncludeUserName:$IncludeUserName -ErrorAction Stop #Add the initial process if ($data.count -eq 0 -or ($r.parent)) { $data += $r $id = $r.Parent.ID } } #try catch { $_ return } } while ($r.parent) if ($id) { #add the top parent if parents found $data += $r } Write-Information $data #Show oldest first to depict a hierarchy $data | ForEach-Object { #insert a new custom typename $_.PSObject.TypeNames.Insert(0, $psName) $_ } | Sort-Object -Property StartTime } #process end { Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" } #end } #close Get-ProcessTree function Show-ProcessTree { [cmdletbinding()] [OutputType('String')] [alias('spt')] param( [Parameter( Mandatory, Position = 0, ValueFromPipelineByPropertyName, HelpMessage = 'Specify a process ID' )] [ValidateNotNullOrEmpty()] [Alias('ProcessID', 'PID')] [int]$ID, [Parameter(HelpMessage = 'Include the process owner.')] [switch]$IncludeUserName ) begin { #tags are used for categorizing the command #cmdTags = ansi,general Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" $reset = "`e[0m" $lineColor = "`e[92m" $nameStyle = "`e[3;93m" $userStyle = "`e[38;5;192m" $idStyle = "`e[38;5;200m" $timeStyle = "`e[38;5;159m" } #begin process { if ($PSEdition -eq 'Desktop') { Write-Warning "This command requires PowerShell 7" Return } Write-Information $PSBoundParameters $p = Get-ProcessTree -ID $id -IncludeUserName:$IncludeUserName if ($p[0] -is [system.diagnostics.process]) { Write-Information $p Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Displaying tree view" $indent = 0 if ($IncludeUserName) { $branch = "$nameStyle{0}$reset -> ID: $idStyle{1}$reset StartTime: $timeStyle{2}$reset [$userStyle{3}$reset]" -f $p[0].ProcessName, $p[0].ID, $p[0].StartTime,$p[0].UserName } else { $branch = "$nameStyle{0}$reset -> ID: $idStyle{1}$reset StartTime: $timeStyle{2}$reset" -f $p[0].ProcessName, $p[0].ID, $p[0].StartTime } #insert a blank lines before and after the tree display for improved readability "`n+{0}" -f $branch for ($i = 1; $i -lt $p.count; $i++) { if ($IncludeUserName) { $branch = "$nameStyle{0}$reset -> ID: $idStyle{1}$reset StartTime: $timeStyle{2}$reset [$userStyle{3}$reset]" -f $p[$i].ProcessName, $p[$i].ID, $p[$i].StartTime,$p[$i].UserName } else { $branch = "$nameStyle{0}$reset -> ID: $idStyle{1}$reset StartTime: $timeStyle{2}$reset" -f $p[$i].ProcessName, $p[$i].ID, $p[$i].StartTime } "{0}$lineColor{1}{5}{2}`n{0}$lineColor{3}{4}{5}:$branch" -f "`e[$($indent)C", [char]0x2502, "`e[1D", [char]0x2514, (([char]0x2500 -as [string]) * 4), $reset $indent += 6 } "`n" } #if process data else { Write-Warning "Failed to find process with process ID $ID." } } #process end { Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" } #end } #EOF |