I have a simeple loop to read for text and also detect the escape ESC [0x1b = chr(27)]
key, to quit.
The Expected Behaviour
<Enter loop>
<Enter any string, such as "AT" and hit Return>
# Some Output
<Repeat above OR>,
...
<Hit the ESC (Escape) key to exit loop>
<Exit Loop>
Actual Behaviour
I have to:
- Hit [Enter] button 2 times, after entering a string and before getting any output.
- ESC key to exit loop does not seem to work.
The Code:
do {
$key = if ($host.UI.RawUI.KeyAvailable) { $host.UI.RawUI.ReadKey('NoEcho, IncludeKeyDown') }
if ($port.IsOpen) {
$at = Read-Host
$port.Write("${at}`r")
} else {
Write-Host -Fo Yellow "[INFO] Port was Closed!"
break
}
} until ($key.VirtualKeyCode -eq 27) # Repeat until a 'ESC'
}
Q: How Can I fix the above to get the intended functionality?
(Why do I need to hit enter 2 times before the input string is processed?)
Experimenting, this one-liner is behaving very weird...
while (1) { if($host.UI.RawUI.ReadKey('IncludeKeyDown').VirtualKeyCode -eq 81) { break };$s=''; $s=Read-Host; if ($s -ne "w") { Write-Host ": $s" -Non | Out-Host } else { "Hit W!"} }
If you want to list the names and codes of all available keys, you can use:
1..255| ForEach-Object { '{0} = {1}' -f $_, [System.Windows.Forms.Keys]$_ }
# <output>
...
16 = ShiftKey
17 = ControlKey
18 = Menu
19 = Pause
...
The following solution preserves the usual
Read-Host
command-line editing experience, so that editing the string being typed by the user is possible:The above waits for a single, initial keypress.
Otherwise:
The initially typed character is sent as a keystroke to the keyboard buffer, where
Read-Host
will pick it up; that is, the edit buffer will be prefilled with the typed character, and it will appear as if the user had just typed that character into the prompt.Caveat: Due to use of
New-Object -ComObject WScript.Shell
, this solution is Windows-only and isn't fully robust: Keeping Shift held down in order to type a sequence of uppercase letters doesn't work reliably - for certain characters, such aso
, everything from the 2nd character unexpectedly remains lowercase[1] - see the next section for a potential workaround IF you have WSL installed.On Linux, you could replace the keystroke-sending +
Read-Host
call more elegantly with (syntax assumes PowerShell 7.3+):On macOS, this doesn't work, unfortunately, and there's no simple workaround.
Note:
Pressing Ctrl-C exits too, but not just the loop, but invariably the entire script (call stack); while you cannot prevent that, you can perform cleanup operations and/or write to any of PowerShell's output streams other than the success output stream in the
finally
block of an enclosingtry
/catch
/finally
statement.Pressing Esc to exit the loop only works before entering the
Read-Host
prompt; that is, once you've typed a printable character, Ctrl-C is your only option to directly abort the prompt - along with your script and all its callers.Read-Host
prompt in realtime, and pressing ESC invariably just clears what has been typed so far, without leaving the prompt.Workaround for the
.SendKeys()
problem on Windows, assuming you have WSL installed:[1] Seemingly, the simulation of keyboard input, which involves programmatically pressing and releasing the
Shift
key, can interfere with properly detecting the physical keyboard state.