How to Properly Pause a PowerShell Script
May 12, 2011 8 Comments
When I first learned about PowerShell, I immediately wanted to convert my batch files into PowerShell scripts and then enhance them with additional features. When I started that process, I quickly learned that PowerShell didn’t have anything equivalent to cmd.exe’s “Pause” command. I looked for a solution online and found the Microsoft TechNet article Windows PowerShell Tip: Press Any Key to Continue. This is the solution recommended by the article:
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
However, the above solution has two things wrong with it:
- It doesn’t work from within Windows PowerShell ISE
- Unlike cmd.exe’s “Pause” command, pressing keys like Ctrl and Alt causes the script to continue
To fix these two issues, I decided to write my own “Pause” function:
Function Pause ($Message = "Press any key to continue . . . ") {
If ($psISE) {
# The "ReadKey" functionality is not supported in Windows PowerShell ISE.
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Click OK to continue.", 0, "Script Paused", 0)
Return
}
Write-Host -NoNewline $Message
$Ignore =
16, # Shift (left or right)
17, # Ctrl (left or right)
18, # Alt (left or right)
20, # Caps lock
91, # Windows key (left)
92, # Windows key (right)
93, # Menu key
144, # Num lock
145, # Scroll lock
166, # Back
167, # Forward
168, # Refresh
169, # Stop
170, # Search
171, # Favorites
172, # Start/Home
173, # Mute
174, # Volume Down
175, # Volume Up
176, # Next Track
177, # Previous Track
178, # Stop Media
179, # Play
180, # Mail
181, # Select Media
182, # Application 1
183 # Application 2
While ($KeyInfo.VirtualKeyCode -Eq $Null -Or $Ignore -Contains $KeyInfo.VirtualKeyCode) {
$KeyInfo = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown")
}
Write-Host
}
Here’s the minified version:
Function Pause($M="Press any key to continue . . . "){If($psISE){$S=New-Object -ComObject "WScript.Shell";$B=$S.Popup("Click OK to continue.",0,"Script Paused",0);Return};Write-Host -NoNewline $M;$I=16,17,18,20,91,92,93,144,145,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183;While($K.VirtualKeyCode -Eq $Null -Or $I -Contains $K.VirtualKeyCode){$K=$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")};Write-Host}
Other than the alternate codepath for the ISE, I think:
[void][System.Console]::ReadKey($FALSE)
is similar to the ‘pause’ mechanics.
That should say $TRUE to hide the console echo.
Thank you! You are correct about your code being similar to cmd.exe’s “Pause” mechanics. Although your code is certainly much leaner than the code I have in this blog post, it only ignores *some* of the same keys as cmd.exe’s “Pause” command. One notable key your code doesn’t ignore is the Windows key.
Your loop would work easier and better like this:
DO {
$PressedKey = $Host.UI.RawUI.ReadKey(“NoEcho, IncludeKeyDown”)
} While ($Ignore -Contains $PressedKey.VirtualKeyCode)
Thanks for this. Very useful bit of code for leaving the output up on the screen to view
Hello,
What’s the difference with a simple : read-host “Click Enter to continue ” ??
For starters, read-host “Click Enter to continue ” ?
has no button that states Enter. It has OK and Cancel. Cancel will end up stopping your script. The verbage ‘Press any key’ was always a misnomer in that not every key caused the intended action to take place. However, there are plenty of keys that did. The action of clicking OK is very different than pressing any key that was sensed. And last, read-host pops up a box that may not fit with the authors design of running on a command-prompt type of environment.
Remember, your repliy should answer the author’s request more than steer them to do what you want to do. When someone does not have an answer and wants to suggest their alternative they should declare that is what they are doing.
Lots of googling finally led me to
# The “ReadKey” functionality is not supported in Windows PowerShell ISE.
Ah, now I understand. Your pause function is exactly what I needed!