Search code examples
batch-filespecial-charactersbatch-choice

How to use special characters in choice command - Batch File


I would like to add special characters like < | into my choice command, how can I do that?

Here's my old code:

CHOICE /C ABCDEFGHIJKLMNOPQRSTUVWXYZ0134567928 /N /M "Press 8 ...."

and I would like it to be something like :

CHOICE /C `~,.<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ0134567928 /N /M "Press 8 ...."

Any help will be appreciated, thanks.

EDIT: Since some of you may ask why do I need so many choice, I'm here to answer. It is because pressing an invalid choice would give you an annoying beep that I would not like. Therefore I asked this question.


Solution

  • The CHOICE command only allows alpha-numeric characters, as well as extended ASCII characters with decimal byte codes between 128-254.

    I have written a pure batch substitute for CHOICE based on a hack that uses REPLACE. My :getKey routine can accept most any character, and allows you to specify the allowed characters, and it puts the captured character in a variable of your choosing - much more convenient than messing with ERRORLEVEL.

    I originally posted the routine at http://www.dostips.com/forum/viewtopic.php?f=3&t=7396. The link also has a more complicated routine that can read absolutely any key value, including NULL, as well as a routine that allows input of a masked string (displays asterisks for each character).

    Below is the :getKey routine, along with an example showing usage. Remove the /I switch if you want the keys to be case sensitive. Full documentation is embedded within the script.

    Edit - I've updated the code to version 2.0 with the ability to specify a prompt, and to have the valid key list be case sensitive. The only CHOICE feature that cannot be emulated is a timeout option with a default response. I don't see any reasonable way to ever provide a timeout option.

    @echo off
    setlocal enableDelayedExpansion
    
    set "valid=0`~,.<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ0134567928"
    call :getKey /p "Press 8 ...." key valid /i
    echo(
    echo You pressed !key!
    exit /b
    
    ::getKey  [/P "Prompt"]  KeyVar  [ValidVar [/I]]
    ::
    :: Read a keypress representing a character between 0x00 and 0xFF and store the
    :: value in variable KeyVar. Null (0x00), LineFeed (0x0A), and Carriage Return
    :: (0x0D) will result in an undefined KeyVar. On Windows 10, Ctrl-Z (0x1A) will
    :: also result in an undefined KeyVar. The simplest way to get an undefined
    :: KeyVar is to press the [Enter] key.
    ::
    :: The optional /P parameter is used to specify a "Prompt" that is written to
    :: stdout, without a newline. Also, the accepted character is ECHOed after the
    :: prompt if the /P option was used.
    ::
    :: The optional ValidVar parameter defines the values that will be accepted.
    :: If the variable is not given or not defined, then all characters are accepted.
    :: If given and defined, then only characters within ValidVar are accepted. The
    :: first character within ValidVar should either be 0, meaning ignore undefined
    :: KeyVar, or 1, meaning accept undefined KeyVar. The remaining characters
    :: represent themselves. For example, a ValidVar value of 0YN will only accept
    :: uppercase Y or N. A value of 1YN will additionally accept [Enter] etc.
    ::
    :: If ValidVar is followed by the optional /I switch, then case of standard
    :: English letters is ignored. The case of the pressed key is preserved in
    :: the result, but English letters A-Z and a-z are not rejected due to case
    :: differences when the /I switch is added.
    ::
    :: Any value (except null) may be entered by holding the [Alt] key and pressing
    :: the appropriate decimal code on the numeric keypad. For example, holding
    :: [Alt] and pressing numeric keypad [1] and [0], and then releasing [Alt] will
    :: result in a LineFeed.
    ::
    :: The only way to enter a Null is by holding [Ctrl] and pressing the normal [2]
    ::
    :: An alternate way to enter control characters 0x01 through 0x1A is by holding
    :: the [Ctrl] key and pressing any one of the letter keys [A] through [Z].
    :: However, [Ctrl-A], [Ctrl-F], [Ctrl-M], and [Ctrl-V] will be blocked on Win 10
    :: if the console has Ctrl key shortcuts enabled.
    ::
    :: This function works properly regardless whether delayed expansion is enabled
    :: or disabled.
    ::
    :: :getKey version 2.0 was written by Dave Benham, and originally posted at
    :: http://www.dostips.com/forum/viewtopic.php?f=3&t=7396
    ::
    :: This work was inspired by posts from carlos and others at
    :: http://www.dostips.com/forum/viewtopic.php?f=3&t=6382
    ::
    :getKey
    setlocal disableDelayedExpansion
    if /i "%~1" equ "/P" (
      <nul set /p ^"=%2"
      shift /1
      shift /1
      set "getKey./P=1"
    ) else (
      set "getKey./P="
    )
    :getKeyRetry
    (
      endlocal&setlocal disableDelayedExpansion
      (for /f skip^=1^ delims^=^ eol^= %%A in ('replace.exe ? . /u /w') do for /f delims^=^ eol^= %%B in ("%%A") do (
        endlocal
        if "%%B" equ "" (set "%1=^!") else set "%1=%%B"
        setlocal enableDelayedExpansion
      )) || (
        endlocal
        set "%1="
        setlocal enableDelayedExpansion
      )
      set "getKey./P=%getKey./P%"
      if defined %1 (set "getKey.key=!%1!") else set "getKey.key=x"
    )
    (
      if "!%2!" neq "" (
        if defined %1 (
          set "getKey.mask=!%2:~1!"
          if not defined getKey.mask goto :getKeyRetry
          if /i "%~3" equ "/I" (
            if "!%1!" equ "=" (
              set "getKey.mask=a!getKey.mask!"
              for /f "delims=" %%A in ("!getKey.mask!") do if /i "!getKey.mask:%%A=%%A!" equ "!getKey.mask!" goto :getKeyRetry
            ) else for /f delims^=^ eol^= %%A in ("!%1!") do if "!getKey.mask:*%%A=!" equ "!getKey.mask!" goto :getKeyRetry
          ) else (
            for /f tokens^=1*^ eol^=^%getKey.key%^ delims^=^%getKey.key% %%A in ("!getKey.mask!!getKey.mask!") do if "%%B" equ "" goto :getKeyRetry
          )
        ) else if "!%2:~0,1!" equ "0" goto :getKeyRetry
      )
      if defined getKey./P echo(!%1!
      exit /b
    )