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-Hostcommand-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-Hostwill 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-Hostcall 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
finallyblock of an enclosingtry/catch/finallystatement.Pressing Esc to exit the loop only works before entering the
Read-Hostprompt; 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-Hostprompt 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
Shiftkey, can interfere with properly detecting the physical keyboard state.