Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Friday Fun: PowerShell Countdown

Posted on April 27, 2012

Recently, Josh Atwell posted a PowerShell script that performed a countdown. Naturally, I was inspired and did the whole "embrace and extend" thing. Don't get me wrong: Josh's script is perfectly fine. I saw some opportunities to try some things and use it as a teaching device. If nothing else, let's have a little fun.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

First off, here's my version, then I'll go over a few highlights.


Function Start-Countdown {
<# comment based help is in the download version #>

Param(
[Parameter(Position=0,HelpMessage="Enter seconds to countdown from")]
[Int]$Seconds = 10,
[Parameter(Position=1,Mandatory=$False,
HelpMessage="Enter a scriptblock to execute at the end of the countdown")]
[scriptblock]$Scriptblock,
[Switch]$ProgressBar,
[Switch]$Clear,
[String]$Message = "Blast Off!"
)

#save beginning value for total seconds
$TotalSeconds=$Seconds

#get current cursor position
$Coordinate = New-Object System.Management.Automation.Host.Coordinates
$Coordinate.X=$host.ui.rawui.CursorPosition.X
$Coordinate.Y=$host.ui.rawui.CursorPosition.Y

If ($clear) {
Clear-Host
#find the middle of the current window
$Coordinate.X=[int]($host.ui.rawui.WindowSize.Width/2)
$Coordinate.Y=[int]($host.ui.rawui.WindowSize.Height/2)
}

#define the Escape key
$ESCKey = 27

#define a variable indicating if the user aborted the countdown
$Abort=$False

while ($seconds -ge 1) {

if ($host.ui.RawUi.KeyAvailable)
{
$key = $host.ui.RawUI.ReadKey("NoEcho,IncludeKeyUp,IncludeKeyDown")

if ($key.VirtualKeyCode -eq $ESCkey)
{
#ESC was pressed so quit the countdown and set abort flag to True
$Seconds = 0
$Abort=$True
}
}

If($ProgressBar){
#calculate percent time remaining, but in reverse so the progress bar
#moves from left to right
$percent=100 - ($seconds/$TotalSeconds)*100
Write-Progress -Activity "Countdown" -SecondsRemaining $Seconds -Status "Time Remaining" -PercentComplete $percent
Start-Sleep -Seconds 1
} Else {
if ($Clear) {
Clear-Host
}
$host.ui.rawui.CursorPosition=$Coordinate
#write the seconds with padded trailing spaces to overwrite any extra digits such
#as moving from 10 to 9
$pad=($TotalSeconds -as [string]).Length
if ($seconds -le 10) {
$color="Red"
}
else {
$color="Green"
}
Write-Host "$(([string]$Seconds).Padright($pad))" -foregroundcolor $color
Start-Sleep -Seconds 1
}
#decrement $Seconds
$Seconds--
} #while

if (-Not $Abort) {
if ($clear) {
#if $Clear was used, center the message in the console
$Coordinate.X=$Coordinate.X - ([int]($message.Length)/2)
}

$host.ui.rawui.CursorPosition=$Coordinate

Write-Host $Message -ForegroundColor Green
#run the scriptblock if specified
if ($scriptblock) {
Invoke-Command -ScriptBlock $Scriptblock
}
}
else {
Write-Warning "Countdown aborted"
}
} #end function

The basic premise hasn't really changed. Run the function specifying the number of seconds to count down and at the end of the countdown display a message. I thought why not really do something? So I added an optional Scriptblock parameter. At the end of the countdown, it will be executed using Invoke-Command.


if ($scriptblock) {
Invoke-Command -ScriptBlock $Scriptblock
}

Next, I decided to add a way to abort the countdown. If you run the function in the ISE and use the Progress bar parameter, you can click the Stop button. In the console, you could press Ctrl+C but that seemed a little heavy handed to me so I inserted code to watch for a key press, specifically the ESC key. If the key press is detected, I set a flag variable to False and the seconds to 0 to break out of the While loop.


#define the Escape key
$ESCKey = 27

#define a variable indicating if the user aborted the countdown
$Abort=$False

while ($seconds -ge 1) {

if ($host.ui.RawUi.KeyAvailable)
{
$key = $host.ui.RawUI.ReadKey("NoEcho,IncludeKeyUp,IncludeKeyDown")

if ($key.VirtualKeyCode -eq $ESCkey)
{
#ESC was pressed so quit the countdown and set abort flag to True
$Seconds = 0
$Abort=$True
}
}
...

After the loop I inserted commands to check if the countdown was aborted or not. If not, then I display the message and run the scriptblock, if supplied.


if (-Not $Abort) {
if ($clear) {
#if $Clear was used, center the message in the console
$Coordinate.X=$Coordinate.X - ([int]($message.Length)/2)
}

$host.ui.rawui.CursorPosition=$Coordinate

Write-Host $Message -ForegroundColor Green
#run the scriptblock if specified
if ($scriptblock) {
Invoke-Command -ScriptBlock $Scriptblock
}
}
else {
Write-Warning "Countdown aborted"
}

I like the use of Write-Progress. I don't think it gets enough attention. I decided to add in the progress bar which is incremented based on a percentage of how much time is to go.


$percent=100 - ($seconds/$TotalSeconds)*100
Write-Progress -Activity "Countdown" -SecondsRemaining $Seconds -Status "Time Remaining" -PercentComplete $percent

I'm subtracting the percent value from 100 so that the progress bar moves from left to right. It looked odd to me to move from "Full" to "Empty", although I suppose for a countdown that is more accurate. You can switch this back.

My last change is how the countdown is handled in the console, if you don't use the progress bar. In Josh's version he cleared the screen and then wrote the current time, clearing the screen each time. The effect is you see the number change in the upper left corner of the console. But what if you don't want to clear the screen? Writing the number will use a new line for each value. Using Write-Host with -NoNewLine will simply append to the current line and look ugly. So I decided to have some fun with raw host data.

First, I grab the coordinates of the cursor's current position using the RawUI property from $host.


#get current cursor position
$Coordinate = New-Object System.Management.Automation.Host.Coordinates
$Coordinate.X=$host.ui.rawui.CursorPosition.X
$Coordinate.Y=$host.ui.rawui.CursorPosition.Y

I want this because I can use the coordinates to put the cursor to a specific location in the console.


#write the seconds with padded trailing spaces to overwrite any extra digits such
#as moving from 10 to 9
$pad=($TotalSeconds -as [string]).Length
if ($seconds -le 10) {
$color="Red"
}
else {
$color="Green"
}
Write-Host "$(([string]$Seconds).Padright($pad))" -foregroundcolor $color
Start-Sleep -Seconds 1
}

I also added code and logic so that the number is displayed in Green using Write-Host until it reaches 10, at which point it is written to the console in red.

I also use the coordinates for some fun if you use -Clear. I put the countdown timer in the center of the console window. In order to do that I had to find the dimensions of the window and divide by 2, treating each value as an [INT].


#find the middle of the current window
$Coordinate.X=[int]($host.ui.rawui.WindowSize.Width/2)
$Coordinate.Y=[int]($host.ui.rawui.WindowSize.Height/2)

Finally, I decided to also center the message. This meant figuring out the message length and then adjusting the X coordinate.


if ($clear) {
#if $Clear was used, center the message in the console
$Coordinate.X=$Coordinate.X - ([int]($message.Length)/2)
}
$host.ui.rawui.CursorPosition=$Coordinate

Curious to see what this looks like? (the clip has no audio)

You can download Countdown2 and load the Start-Countdown function into your PowerShell session. Be sure to read the help.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

3 thoughts on “Friday Fun: PowerShell Countdown”

  1. Eric Iverson says:
    April 27, 2012 at 10:08 am

    What is this all about? FZ8D4JAWU7Q3 Post and Technorati Claim – ignore me ?

    1. Jeffery Hicks says:
      April 27, 2012 at 10:18 am

      Ignore that. Trying to get my blog “claimed” on Technorati but it is turning into more work than it is probably worth.

      1. Eric Iverson says:
        April 27, 2012 at 10:20 am

        🙂 Figured that it was something like that. Keep up the good work.

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d