aGerman wrote:Wouldn't it be awesome to work with arrow keys in a Batch file?
Good question. Problem is that most command line utilities work at the "standard streams" interfaces, and rightfully so since that's what allows the usual redirection and piping. However, the standard streams are character based, while arrow keys - along with shift/control/caps/Fx/etc keys - are not mapped to characters. One can't redirect an arrow-down to a file, or pipe in an F1. The low-level key-oriented input in Windows is event-based, and that's where those special keys queue each of their down/repeat/up state changes. Then, at the char-oriented high-level, the respective events are synthesized into meaningful characters as appropriate. That's how keystrokes get translated into an "a" or "A" depending on the CAPS and Shift states, or ALT+49 magically generates an "1".
I don't think that low-level keyboard input can be done all in batch. One way to cheat is powershell, and below is a hybrid batch doing just that. (Note: it requires powershell v2 to support the <# .. #> comment syntax, which comes with windows v7+ but may require a separate download
http://support.microsoft.com/kb/968929 for xp.)
Code: Select all
<# :: --------------------------------------------------------------- .cmd ----
@echo off & setlocal
set "ps=" & for %%X in (powerShell.exe) do (
set "ps=%%~$PATH:X")
if not defined ps (
set "ps=%systemRoot%\system32\windowsPowerShell\v1.0\powerShell.exe")
echo(
echo Press ESC to exit, Ctrl-Break to abort...
echo(
:loop
for /f "tokens=1-7 delims= " %%A in (
'^<"%~f0" "%ps%" -ExecutionPolicy bypass -NoProfile -Command -'
) do (
echo key [%%~A %%~B] char '%%~C' state [%%~D %%~E] down [%%~F %%~G]
if '%%~A'=='27' endlocal & exit /b 0
)
goto :loop & rem ---------------------------------------------------- .ps1 --#>
# keysConverter.convertToString only used for displaying key's friendly name
Add-Type -AssemblyName System.Windows.Forms
$xvt = New-Object -TypeName System.Windows.Forms.KeysConverter
$key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp,AllowCtrlC")
Write-Output("`"$($key.VirtualKeyCode)`"" `
+ " `"$($xvt.ConvertToString($key.VirtualKeyCode))`"" `
+ " `"$($key.Character)`"" `
+ " `"$([int]$key.ControlKeyState)`"" `
+ " `"$($key.ControlKeyState -replace `" `",`"`")`"" `
+ " `"$([int]$key.KeyDown)`"" + " `"$($key.KeyDown)`"")
exit 0
Sample output for <arrow-left><up><right><down>a<shift>a<ctrl>a<alt>49<esc>.
Code: Select all
C:\tmp>keyDown-ps.cmd
Press ESC to exit, Ctrl-Break to abort...
key [37 Left] char '' state [ ] down [ ]
key [37 Left] char '' state [ ] down [ ]
key [38 Up] char '' state [ ] down [ ]
key [38 Up] char '' state [ ] down [ ]
key [39 Right] char '' state [ ] down [ ]
key [39 Right] char '' state [ ] down [ ]
key [40 Down] char '' state [ ] down [ ]
key [40 Down] char '' state [ ] down [ ]
key [65 A] char 'a' state [0 0] down [1 True]
key [65 A] char 'a' state [0 0] down [0 False]
key [16 ShiftKey] char '' state [ ] down [ ]
key [65 A] char 'A' state [16 ShiftPressed] down [1 True]
key [65 A] char 'A' state [16 ShiftPressed] down [0 False]
key [16 ShiftKey] char '' state [ ] down [ ]
key [17 ControlKey] char '' state [ ] down [ ]
key [65 A] char '☺' state [8 LeftCtrlPressed] down [1 True]
key [65 A] char '☺' state [8 LeftCtrlPressed] down [0 False]
key [17 ControlKey] char '' state [ ] down [ ]
key [18 Menu] char '' state [ ] down [ ]
key [37 Left] char '' state [ ] down [ ]
key [37 Left] char '' state [ ] down [ ]
key [33 PgUp] char '' state [ ] down [ ]
key [33 PgUp] char '' state [ ] down [ ]
key [18 Menu] char '1' state [0 0] down [0 False]
key [27 Escape] char '←' state [0 0] down [1 True]
C:\tmp>
Some notes:
- meant as a proof of concept, but quite slow for practical use;
- could be sped up by dropping the '$xvt.ConvertToString' call, and the 'add-type' along with it (the virtual key codes themselves can be looked up at
http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx);
..even more by dropping 'IncludeKeyUp' since most times it's KeyDown that's the event of interest;
..far more by moving (some of) the loop to .ps code instead of running powershell for each individual keyboard event;
- besides the keyboard topic, the above also demonstrates a generic hybrid batch/powershell script layout;
..since powershell won't execute scripts with an extension other than .ps1, the trick in this case was to use the "-Command -" switch for using the standard input, then have the standard input redirected to read "%~f0".
EDIT: Thanks to npocmaka_ for pointing the thread at http:
//www.dostips.com/forum/viewtopic.php?f=3&t=5526 (sorry, not a live link since I exhausted my allowance of URLs per post already) where cmd/ps hybrids were previously discussed, including the "<# :" comment/label trick (npocmaka_), and the "powershell -c -" line to run a script from the standard input in order to work around the requirement of a .ps1 extension for actual files (siberia-man, piped-in "type"). [
end edit]
Liviu