Top is a super-cool interactive tool. It displays real-time processes and resources. And you can interact with them. Linux guys always have the shiniest command prompts… pfff. Let’s try to fix that!

TLDR… Right to some code!

What do I want from Top on Windows?

First, it should look the part. Nice graphics, colors, quick refresh. No jerky, bland generic stuff.

Then, it also needs to be functional. At the moment, the script below does not include interactive functionality. It would be great to select different processes or tasks and start, restart or stop them.

Lastly, I would like to make it easily expandable. You want to display each CPU core separately? Or maybe you want up- and download speeds ready in your command line. Should be easy 🙂

Where am I now?

In Belgium… whut? Ow, that was not the question?

I’ve tried to create some modular graphing functions that accept integers as input.G et-Graph and Get-Metricsbar can output graphics to the host.

Get-Graph

Get-Graph outputs a 2D graph. It is mainly a stripped-down version of Show-Graph made by Prateek Singh. He made a wicked nice script that takes an array of integers as input. It then outputs a beautiful 2D chart with labels, values, and all kinds of colors.

Nice work, Prateek!

Here’s our stripped-down version:

Get-Metricbar

Then, we have Get-Metricbar. Metricsbar uses a process-block that accepts an array containing performance data.

Big thanks to Bis for cleaning, troubleshooting string formatting, and some general PowerShelly changes!

Yippie! Bars.

You can add as many bars as you like. Just add them to the $CurrentLoad array.

Show-PerformanceMetrics

$CurrentLoad is our workhorse. It does all the heavy computing.

Each metric we want can be added easily here.

Here’s the code

function Get-MetricBar {
 
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True, Position=0, ValueFromPipelineByPropertyName)]
        [Alias('Key')]
        $Metric,
       
        [Parameter(Mandatory=$True, Position=1, ValueFromPipelineByPropertyName)]
        [Alias('Value')]
        $MetricValue
    )

    Process {
        # Generate string respresenting metric bar for the metric in the pipeline.
        -join @(
            $Metric.toUpper()
            ': '
            ' ' * (20 - $Metric.Length)
            '▓' * $MetricValue
            '░' * (100 - $MetricValue)
        )
    }
}

Function Get-Graph {
    [cmdletbinding()]
    Param(
            [Parameter(Mandatory=$true)]
            [int[]] $Datapoints
    )

    # Create a 2D Array to save datapoints in a 2D format
    $Array = New-Object 'object[,]' ($Datapoints.Count + 1), 10
    $Counter = 0

    $Datapoints | ForEach-Object {
        $DatapointRounded = [Math]::Floor($_/10)
        
        1..$DatapointRounded | ForEach-Object {
            $Array[$Counter,$_] = 1
        }
        $Counter++
    }
 
    # Draw graph by drawing each row line-by-line, top-to-bottom.
    ForEach ($RowHeigth in (10..0)) {
        
        #Assembly of each row.
        $Row = ''

        Foreach ($DatapointLocation in (0..($BufferSizeWidth -1))) {
            if ($null -eq $Array[$DatapointLocation,$RowHeigth]) {
                $Row = [string]::Concat($Row, '░')
            }
            else {
                $Row = [string]::Concat($Row, '▓')
            }   
        }

        # To color the graph depending upon the datapoint value.
        switch ($RowHeigth) {
            {$RowHeigth -gt 7} { Write-Host $Row -ForegroundColor DarkRed }
            {$RowHeigth -le 7 -and $RowHeigth -gt 4} { Write-Host $Row -ForegroundColor DarkGray }
            {$RowHeigth -le 4 -and $RowHeigth -ge 1} { Write-Host $Row -ForegroundColor DarkCyan }
        }
    }
}

function Show-PerformanceMetrics {
    Clear-Host

    $BufferSizeWidth = $host.UI.RawUI.BufferSize.Width
    $LoadHistory = @()

    while (1) {
        # Calculate different metrics. Can be updated by editing the value-key pairs in $Currentload.
        $OS = Get-Ciminstance Win32_OperatingSystem
        $CurrentLoad = [ordered]@{
            "CPU Load" = (Get-CimInstance win32_processor).LoadPercentage
            "RAM Usage" = (100 - ($OS.FreePhysicalMemory/$OS.TotalVisibleMemorySize)*100)
            "Pagefile Usage" = (100 - ($OS.FreeVirtualMemory/$OS.TotalVirtualMemorySize)*100)
        }

        $LoadHistory += $CurrentLoad
 

        # Reset cursor and overwrite prior output
        $host.UI.RawUI.CursorPosition = @{x=0; y=1}

        # Output on screen
        Get-Graph -Datapoints ($LoadHistory."CPU Load" | Select-Object -Last $BufferSizeWidth)
        
        Write-host ""
        
        $CurrentLoad.GetEnumerator() | Get-MetricBar | Write-Host -ForegroundColor "DarkCyan"
        
        Write-host ""
        
        Get-Process | Sort-Object CPU -desc | Select-Object -first 5 | Format-Table -RepeatHeader
    }
}