0

objective: i want user to type just a single character (any character) and then the script immediately responds without additional keystroke of "enter" key. if user type "enter" key without any character previously entered, the script would not respond.

i am stuck with this code:

@Echo Off

SET Alnum=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890

:: ordinary symbol string would not work!
:: it must be escaped, but i don't know how.
SET Symbol= 

SETLOCAL EnableExtensions EnableDelayedExpansion

CALL :CharInput  "Type one char: "
ECHO Your input was: "!RetVal!"
ECHO(

CALL :CharInput  "one more char: "
ECHO Your input was: "!RetVal!"
ECHO(

CALL :CharInput  "still need more: "
ECHO Your input was: "!RetVal!"
ECHO(

GOTO :eof


:: @param1 %~1     message to print
:: @return RetVal  the single character entered by user
:CharInput
    IF "" == "!VisibleChars!" (
        CALL :CharacterizedWord  "%Alnum%%Symbol%"
        SET VisibleChars=!RetVal!
    )
    SET /P "=%~1" < Nul

    SET Found=false

    FOR /F "skip=1" %%# IN ('REPLACE "%~f0" . /U /W') DO SET "CHR=%%#"
    SET /P "=!CHR!" <Nul

    FOR %%a in (!VisibleChars!) DO (
        IF "%%a" == "!CHR!" (
            SET Found=true
        )
    )

    :: needs work around to make the script print only 1 message
    :: each time the user presses the "enter" key
    :: without any visible characters previously entered
    IF "false" == "!Found!" (
        ECHO(
        GOTO :CharInput
    ) ELSE (
        SET "RetVal=!CHR!"
        ECHO(
    )
GOTO :eof


:: @param1 %~1     the word being characterized
:: @return RetVal  the string contains words each of single character
:CharacterizedWord
    SET Word=%~1
    SET tempstr=%~1
    SET count=0
    :CharacterizedWordLoop
    IF DEFINED tempstr (
        SET tempstr=%tempstr:~1%
        SET /a count+=1
        SET /a pos=%count%-1
        SET RetVal=!RetVal! !Word:~%pos%,1!
        GOTO :CharacterizedWordLoop
    )
GOTO :eof
  1. I have tried any combination to collect all symbol chars into a single word. Can anyone help me to fill the emptiness of "Symbol" var?

  2. Is there any better algorithm other than this to expand the visible character list without editing the the list each time i find new character?

  3. This app is still print "Type one char: " message each time user presses the "enter" key. Can anyone fix this?

  • 2
    Try this? https://stackoverflow.com/questions/13166100/how-can-i-return-a-single-character-from-the-console-in-a-batch-script-without-p Looks like its called CHOICE – Ctznkane525 Dec 19 '17 at 01:52
  • @John Kane: how about symbol? native choice don't support symbol. – the liquid metal Dec 19 '17 at 02:19
  • 3
    this has some info on how to use special characters on choice. It's technically not supported - but a workaround https://stackoverflow.com/questions/41456194/how-to-use-special-characters-in-choice-command-batch-file – Ctznkane525 Dec 19 '17 at 02:26

1 Answers1

0

In the console, the $host ReadKey method can be used to read a single key with no Enter key needed. Does this do something like you want?

Put this into a file named "thekey.ps1".

<# Ignore:
    Enter
    Shift
    Ctrl
    Alt 18
    CapsLock
    LeftArrow
    RightArrow
    UpArrow
    DownArrow
    Delete
    End 35
    Home 36
    Insert 45
    PgUp 33
    PgDown 34
    Windows 91
#>

$nowatch = $(13, 16, 17, 18, 20, 37, 38, 39, 40, 46, 35, 36, 45, 33, 34, 91)

do {
    $key = $host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')

    #$key.VirtualKeyCode

    $vkc = $key.VirtualKeyCode
    $k = $key.Character
} while ($nowatch -contains $vkc)
$k

If there are other keys you need to discover, remove the comment '#' from #$key.VirtualKeyCode and run .\thekey.ps1 in a PowerShell console (not ISE).

Put this into a file named "thekey.bat".

@ECHO OFF
:Head
FOR /F "usebackq tokens=*" %%k IN (`powershell -NoProfile -File .\thekey.ps1`) DO (
    SET "THEKEY=%%k"
)
ECHO THEKEY is %THEKEY%
GOTO Head

Alternatively:

It might be easier to specify the keys you want to recognize rather than those you don't.

$keylist = @()
0x21..0x7E | ForEach-Object { $keylist += $_ }

do {
    $key = $host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')

    #$key.VirtualKeyCode

    $vkc = $key.VirtualKeyCode
    $k = $key.Character
} while ($keylist -notcontains $vkc)
$k
lit
  • 14,456
  • 10
  • 65
  • 119
  • the script immediately responds to "sift" key pressed, causing "THEKEY is" printed several times before the companion key. This is not the behaviour which i want. – the liquid metal Dec 20 '17 at 05:42
  • @theliquidmetal - Please check the revisions I made to the code. I hope this is closer to what you are seeking. – lit Dec 20 '17 at 21:05