Functions/Public/Watch-Command.ps1

#!/usr/bin/env pwsh

<#
.SYNOPSIS
    Loop through a command forever until canceled (Ctrl + C)
.DESCRIPTION
    This is meant to be a powershell equivalent to the linux `watch` command.
.PARAMETER Command
    This parameter takes in a command to evaluate. This parameter takes in a string, and uses Invoke-Expression to run the command. This means that complex commands must be wrapped within quotation marks.
.PARAMETER WaitSeconds
    This parameter takes in an Int (number) which equates to the number of seconds to wait after the completion of the command before executing again.
.PARAMETER Differences
    This switch will not overwrite the original text displayed if something in the output has changed. It will place a timestamp in between the previous output and the current (changed) output. This also breaks out what items were `Added` or `Removed` from the previous output to assist with monitoring visually.
.EXAMPLE
    PS> Watch-Command -Command Get-Process
 
    Description
    -----------
    This will run Get-Process, wait 5 seconds (the default amount of time) and run it again.
.EXAMPLE
    PS> Watch-Command "Get-Process | Select-Object -First 12" -Differences -WaitSeconds 3
 
    Description
    -----------
    This will run the Get-Process command through the pipeline, and monitor for differences every 3 seconds. Notice this command is treated as a string and `"wrapped in quotes"`
.NOTES
    Author: Matthew J. DeGarmo
    Handle: @matthewjdegarmo
#>

function Watch-Command() {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string] $Command,

        [Parameter(Position = 1)]
        [int] $WaitSeconds = 5,

        [switch] $Differences
    )
    begin {
        $Output = $null
        $PreviousOutput = $null
        $Difference = $null
        $SaveX = [console]::CursorLeft
        $SaveY = [console]::CursorTop + 1
    }
    process {
        try {                
            Write-Output "Watching command: '$Command' | Interval: $WaitSeconds`s | Time: $([datetime]::Now)"
            While ($true) {
                [console]::SetCursorPosition($SaveX, $SaveY)
                $Output = (Invoke-Expression -Command $Command -ErrorAction SilentlyContinue)
                if ($PreviousOutput -and $Output -and $Differences.IsPresent) {
                    # $Properties = $PreviousOutput | Get-Member -MemberType "*Property*"
                    $Difference = (Compare-Object $PreviousOutput $Output -PassThru)
                    if ($Difference) {
                        ($PreviousOutput | Out-String).Trim()
                        "|-------------------------------| |-----------------|"
                        "There was a change in the output: $([datetime]::Now)"
                        "|-------------------------------| |-----------------|"
                        $AddedDifferences = $Difference | Where-Object { $_.SideIndicator -eq "=>" }
                        $RemovedDifferences = $Difference | Where-Object { $_.SideIndicator -eq "<=" }
                        if ($AddedDifferences) { "Added:"; ($AddedDifferences | Out-String).Trim(); "" }
                        if ($RemovedDifferences) { "Removed:"; ($RemovedDifferences | Out-String).Trim(); "" }
                        # ($Difference | Out-String).Trim()
                        # $Difference = $null
                        # $AddedDifferences = $null
                        # $RemovedDifferences = $null
                        ""; Write-Output "Watching command: '$Command' | Interval: $WaitSeconds`s | Time: $([datetime]::Now)"
                        $SaveX = [console]::CursorLeft
                        $SaveY = [console]::CursorTop
                    }
                }
                if ($Differences.IsPresent) {
                    $PreviousOutput = $Output
                }
                ($Output | Out-String).Trim()
                Start-Sleep -Seconds $WaitSeconds
            }
        }
        finally {
            $SaveX = $null
            $SaveY = $null
            $Output = $null
            $PreviousOutput = $null
            # $Difference = $null
        }
    }
    end {}
}