3

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.

dan1st
  • 12,568
  • 8
  • 34
  • 67
  • 1
    You need to escape or quote special characters. If you do you will see this message - `The valid choice characters are: a-z, A-Z, 0-9 and ASCII values of 128 to 254` –  Jan 04 '17 at 04:35
  • @Noddles uhm. This has been answered below. –  Jan 04 '17 at 04:52
  • 1
    What do you mean. Your code was wrong (lacking THREE double quotes) so I fixed it to show you the error message about the rest of your wrong code. MSDos `choice` takes the escape key. –  Jan 04 '17 at 04:58

3 Answers3

5

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
)
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • I got a command syntax incorrect whenever I run `]: "` –  Jan 04 '17 at 05:26
  • 2
    @SteveFest, I guess there is an `=` sign missing: `]: "` – aschipfl Jan 04 '17 at 10:17
  • @aschipfl - Thanks. I had actually tested the code, but I pasted the wrong version into the answer. All fixed. – dbenham Jan 04 '17 at 12:43
  • @dbenham sorry for the late reply.... I recently used this code again. However is there anyway to add a timeout like the `/t` flag in the `choice` command? –  Feb 05 '17 at 09:52
  • @SteveFest - Sorry, no. – dbenham Feb 05 '17 at 13:24
  • @dbenham at last I created a VB script to simulate a key press so a key is pressed when my own timeout is passed. –  Feb 06 '17 at 10:26
  • @user6250760 can you post your VB script that does timeout - perhaps a pastebin link? – Skip R Nov 13 '19 at 01:06
2

Using Choice, you cannot.

If you attempt to execute Choice with an invalid character, you will get a message. For example (caret '^' symbol is to escape the pipe '|', as this is a special character for cmd):

CHOICE /C ^|

returns:

ERROR: Invalid choice. The valid choice characters are: a-z, A-Z, 0-9 and ASCII values of 128 to 254.

Possible workaround:

You can possibly use SET to accomplish your needs, but then you are working with variables in a batch script that could potentially cause problems. Anywhere that you would use these variables, you will need to preceed with the escape character caret (^).

This solution would also require that the user hit the enter key after making their selection.

@ECHO OFF
Choose one:
ECHO.
ECHO ^| - 1
ECHO ^> - 2
ECHO A - 3
ECHO ^" - 4
ECHO ^^ - 5
SET /P KeyPressed=Choice? [^|^>A]
IF [^%KeyPressed%]==[^|] ECHO 1 && GOTO END
IF [^%KeyPressed%]==[^>] ECHO 2 && GOTO END
IF [^%KeyPressed%]==[^A] ECHO 3 && GOTO END
IF [^%KeyPressed%]==[^"] ECHO 4 && GOTO END
IF [^%KeyPressed%]==[^^] ECHO 5 && GOTO END
ECHO None of them.


:END
Jeff Block
  • 424
  • 2
  • 7
  • 1
    If you are using quotes rather than brackets for comparisons, you do not need escaping; for instance: `if "%KeyPressed%"=="|"` – aschipfl Jan 04 '17 at 10:20
  • @aschipfl - What if %KeyPressed% == " – Jeff Block Jan 04 '17 at 10:31
  • 1
    You almost got me! ;-) I'd place `set "KeyPressed=%KeyPressed:"=""%"` right after `set /P` to double the `"`, then do `if "%KeyPressed%"==""""`... – aschipfl Jan 04 '17 at 10:48
-1
ShowMenu

Sub ShowHelpMenu
    outp.writeline " -----------------------------------------------------------------------------"
    outp.writeblanklines(1) 
    outp.writeline " Menu"
    outp.writeline " ----"
    outp.writeblanklines(1) 
    outp.writeline "  1 Help              2 HTML Help          3 Version           4 History"
    outp.writeblanklines(1) 
    outp.writeline "  5 Exit"
    outp.writeblanklines(1) 
    outp.write "Filter>"
End Sub

'=============================================
Sub ShowMenu
    Do
        ShowHelpMenu
        Answ=Inp.readline
        If Answ = "1" Then
            ShowGeneralHelp "TEXT"
        Elseif Answ = "2" Then
            ShowGeneralHelp "HTML"
        Elseif Answ = "3" Then
            Version
        Elseif Answ = "4" Then
            History
        Elseif Answ = "5" Then
            Exit Do
        End If
    Loop
End Sub

Th9is is a VBS file.

  • Sorry. I would like a answer for Batch file. Your answer is in VBS. –  Jan 04 '17 at 05:13
  • I actually trimmed the choice command and I forgot to add back the quotes. I am working on another thing that require constantly pressing the keyboard. I don't think `set /p` could fulfill that. –  Jan 04 '17 at 05:17
  • Why I use so much options because I do not want it to **beep** whenever an invalid option is chosen. –  Jan 04 '17 at 05:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/132250/discussion-between-stevefest-and-noodles). –  Jan 04 '17 at 05:22