Hamish Grubijan
Hamish Grubijan

Reputation: 10820

Windows Powershell - how to re-display the statuses of some windows services only when they change?

I have just started with Powershell. I have a bat file which simply kicks off the following PowerShell script, which then re-displays the status of services that I am interested every 5 seconds. It works fine (although I could use some pointers on how to make this cleaner) except that there is a brief annoying flicker every time when the screen is re-painted. So, I would like to change this so that the sleep interval is 1 second or 500ms, but the re-painting is done only when the content changes. Alternatively, if it is easier to repaint a dos screen unconditionally, without causing it to flicker, then I would be happy with that solution as well. Also please help me clean up the code. I am so far afraid of functions, variables etc. in PowerShell because PS frequently yells at me when I try to use C-family/Python syntax and constructs. PS is somehow different from Python, Java, etc. and I have not figured out the philosophy of it yet.

# When you run this script, it will show a simple window with the status of the services;

# Do we want to XYZ as well?
# To assign $true value, use:
#PowerShell.exe .\ShowServices.ps1 -showXYZ:$true
#param([switch]$showXYZ=$false)
param([switch]$showXYZ=$true)

# Build a regex for services
$servicesRegex = "Microsoft.*|Network.*"
if ($showXYZ -eq $true) { $servicesRegex = $servicesRegex + "|XYZ.*" }

# Controlling the appearance of the window
$pshost = get-host
$pswindow = $pshost.ui.rawui

$newsize = $pswindow.buffersize
$newsize.height = 3000
$newsize.width = 50
$pswindow.buffersize = $newsize

$newsize = $pswindow.windowsize
$newsize.height = 10
$newsize.width = 50
$pswindow.windowsize = $newsize

$global:CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
#$global:ComputerName = gc env:computername
#$pswindow.WindowTitle = "Service statuses for {0} on {1}." -f $CurrentUser.Name, $ComputerName
$pswindow.WindowTitle = "Service statuses for {0}." -f $CurrentUser.Name

# Clear the screen once
clear

# Formatting details.
[int]$global:len1 = 35
[int]$global:len2 = 8
[int]$global:sleepInterval = 5 #seconds - I want this to be more frequent, but not annoying.

function printHeader
{
  Write-Host("") # Blank line
  [string]$line = "{0,-$global:len1}  {1,-$global:len2}" -f "Service Name", "Status"
  Write-Host $line
  Write-Host("_" * $global:len1 + "  " + "_" * $global:len2)
}

function printService($serviceObject)
{
  [string]$foreColor = "yellow" # Default color, if neither Stopped nor Running
  if ($serviceObject.status -eq "Stopped") {$foreColor = "red" }
  if ($serviceObject.status -eq "Running") {$foreColor = "green" }
  [string]$outStr = "{0,-$global:len1}  {1,-$global:len2}" -f $serviceObject.displayname, $serviceObject.status
  Write-Host $outStr -foregroundcolor $foreColor #-backgroundcolor white
}

# The meat of it.
while($true)
{
  printHeader
  Get-Service | Where-Object {$_.name -match $servicesRegex} | ForEach-Object { printService($_) }
  Start-Sleep -s $global:sleepInterval # Sleep x seconds
  clear
}

Upvotes: 2

Views: 882

Answers (1)

Andrew Savinykh
Andrew Savinykh

Reputation: 26270

Try changing the last part of you script to this:

# The meat of it.
$data = @()
while($true)
{
    $new = Get-Service | Where-Object {$_.name -match $servicesRegex}
    if (Compare-Object $data $new -Property Status) { 
        $data = $new
        clear
        printHeader
        $data | ForEach-Object { printService($_) }
    }
    Start-Sleep -s $global:sleepInterval # Sleep x seconds    
}

Upvotes: 3

Related Questions