Reputation: 21
In my script I need to get 1 character from user and process it immediately whithout waiting for an Enter. Additionally, I want to treat the character case-sensitive.
write-host("Please press a or A:")
$choice = ($host.UI.RawUI.ReadKey(('NoEcho,IncludeKeyUp,IncludeKeyDown'))).character
if($choice -ceq "a")
{
write-host("You pressed a");
}
elseif($choice -ceq "A")
{
write-host("You pressed A");
}
else
{
Write-Host("You pressed neither a nor A")
}
Pause
The issue of this code is when I try to press "A", it shows "You pressed neither a nor A". The reason is to type "A" I have to press Shift first, Powershell detects Shift pressed and it process immediately without waiting for an A.
Anyone has idea how to solve this?
Upvotes: 1
Views: 1022
Reputation: 439737
The simplest solution is to only react to keypresses that result in a printable character, and then evaluate which character was pressed via a switch
statement.
# Wait for a printable character to be pressed.
while (($char=($host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')).Character) -eq 0) {}
# Evaluate it.
switch -CaseSensitive ($char) {
'a' { 'You pressed a' }
'A' { 'You pressed A' }
default { 'You pressed neither a nor A' }
}
Note: While modifier keys Shift, Control, and Alt by themselves do not count as keypresses, combinations with a printable character do; therefore, for instance, Alt-a is treated the same as 'a'
, and Control-a as control character START OF HEADING, U+0001
.
If you want to avoid that, use the following variation instead:
# Wait for a printable character to be pressed, but only if not combined
# with Ctrl or Alt.
while (($key=$host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')).Character -eq 0 -or
$key.ControlKeyState -notin 0, 'ShiftPressed') {}
$char = $key.Character
Note: This works on Windows only - on Unix-like platforms, the .ControlKeyState
property is apparently always 0
.
However, if you use [Console]::ReadKey()
instead, you can make it work on Unix-like platforms too - which assumes that you're willing to assume that your script always runs in a console (terminal), and not other kinds of PowerShell hosts.
# Wait for a printable character to be pressed, but only if not combined
# with Ctrl or Alt.
while (($key = [Console]::ReadKey($true)).KeyChar -eq 0 -or
$key.Modifiers -notin 0, 'Shift') {}
$char = $key.KeyChar
Upvotes: 1
Reputation: 385
Does the below make up for what is expected,
($key = $Host.UI.RawUI.ReadKey()) | % { if ($_.VirtualKeyCode -eq '16') {
$key = $Host.UI.RawUI.ReadKey()
}
$Choice = $key.Character
if ($Choice -ceq "a"){
"`rYou pressed 'a'"
}
elseif ($Choice -ceq "A"){
"`rYou pressed 'A'"
}
else {
"`rYou neither pressed 'a' nor 'A'"
}
}
Upvotes: 1
Reputation: 30183
Try the following code snippet:
if ( $Host.Name -eq 'ConsoleHost' ) {
Write-Host "Please press a or A:"
Do {
$choice = $host.UI.RawUI.ReadKey(14)
} until ( $choice.VirtualKeyCode -in @( 48..90) )
if ( $choice.Character -ceq "a") {
Write-Host "You pressed a";
}
elseif ( $choice.Character -ceq "A") {
Write-Host "You pressed A";
}
else {
Write-Host "You pressed neither a nor A ($($choice.Character))";
}
} else {
# e.g. Windows PowerShell ISE Host:
# the "ReadKey" method or operation is not implemented.
Write-Host '$Host.Name -neq ConsoleHost' -ForegroundColor Magenta
}
As currently written, $choice.VirtualKeyCode -in @( 48..90)
condition allows some (limited) subset of printable characters. Adjust it with respect to Keys Enum…
Upvotes: 1