Page 1 of 1

Alternative to CHOICE - need help

Posted: 02 Nov 2015 12:34
by Jer
I am experimenting with an alternative to CHOICE and have taken code from a post
on this forum that uses xcopy to capture a keystroke. I've tried it with echo on and
it looks like it should work, comparing entries with valid choices, but results are not
consistent.

Choices are 1, 2, 3 or q.
If I enter 1, it tests to be invalid. If I enter 1 again, it is a valid choice.
If I enter 4 (not a valid choice) it tests to be a valid choice, and if I enter 4 again,
it tests to be invalid. If I enter q, it tests not valid but proceeds to quit anyway.

This batch code is just to explore how keystrokes can be trapped, and I hope with your
help, will evolve into a bug-free function.

Thanks for your advice and any other tips about this CHOICE alternative.

Code: Select all

@Echo Off
rem simulate the CHOICE function
setlocal

Set "choices=1, 2, 3, q"

:top
Set KEY=
Echo(&Echo Enter your choice: %choices%

For /f "delims=" %%a In ('Xcopy /l /W "%~f0" "%~f0" 2^>nul') Do (
   If Not defined KEY Set "KEY=%%a"
)

Set KEY=%KEY:~-1%

If "%KEY%"==" " (
   Echo You pressed the space bar.
) Else If defined KEY (
   Set "validEntry=FALSE"
   For %%a In (%choices%) Do If /I "%%a"=="%KEY%" Set "validEntry=TRUE"
   If "%validEntry%"=="TRUE" (Echo Entry is valid: %KEY%) Else Echo Entry not valid: %KEY%
) Else (
   Echo How did this happen?
)

If /I "%KEY%"=="q" GoTo:eof
GoTo:top

endlocal

Re: Alternative to CHOICE - need help

Posted: 02 Nov 2015 13:07
by OperatorGK
Read this thread: viewtopic.php?f=3&t=6382 and rework posted code to suit your needs.

Re: Alternative to CHOICE - need help

Posted: 02 Nov 2015 13:17
by Squashman
OperatorGK wrote:Read this thread: viewtopic.php?f=3&t=6382 and rework posted code to suit your needs.

How does that help with using xcopy?

Re: Alternative to CHOICE - need help

Posted: 02 Nov 2015 13:36
by aGerman
That function may help:

Code: Select all

@echo off &setlocal
Set "choices=123q"

:top
echo(
<nul set /p "=Enter your choice [%choices%]: "
call :choice %choices%
echo Option #%errorlevel% was chosen.
pause
if %errorlevel% neq 4 goto top

exit /b


:choice ByVal_Choices
setlocal DisableDelayedExpansion
set "n=0" &set "c=" &set "e=" &set "map=%~1"
if not defined map endlocal &exit /b 0
for /f "eol=1 delims=" %%i in ('xcopy /lwq "%~f0" :\') do set "c=%%i"
set "c=%c:~-1%"
if defined c (
  for /f delims^=^ eol^= %%i in ('cmd /von /u /c "echo(!map!"^|find /v ""^|findstr .') do (
    set /a "n += 1" &set "e=%%i"
    setlocal EnableDelayedExpansion
    if /i "!e!"=="!c!" (
      echo(!c!
      for /f %%j in ("!n!") do endlocal &endlocal &exit /b %%j
    )
    endlocal
  )
)
endlocal &goto choice

Regards
aGerman

Re: Alternative to CHOICE - need help

Posted: 02 Nov 2015 14:19
by Jer
aGerman, your code works perfectly by prompting until a valid keystroke is pressed.
I am not at a skill level where I can understand much of it, so I'll have to stay within
what I can understand, or almost understand. Your post helped me get rid of the
inconsistencies after I added setlocal emabledelayedexpansion. Thanks.

Code: Select all

@Echo Off
rem test code-simulate the CHOICE function
setlocal

Set "choices=1,2,3,q"

:top
Set KEY=
Echo(&Echo Enter your choice: %choices%

For /f "delims=" %%a In ('Xcopy /l /W "%~f0" "%~f0" 2^>nul') Do (
   If Not defined KEY Set "KEY=%%a"
)

Set KEY=%KEY:~-1%

rem added line:
Setlocal EnableDelayedExpansion

If "%KEY%"==" " (
   Echo You pressed the space bar.
) Else If defined KEY (
   Set "validEntry=FALSE"
   For %%a In (%choices%) Do If /I "%%a"=="%KEY%" Set "validEntry=TRUE"
   If "!validEntry!"=="TRUE" (Echo Entry is valid: %KEY%) Else Echo Entry not valid: %KEY%
) Else (
   Echo How did this happen?
)
rem added line:
Endlocal

If /I "%KEY%"=="q" GoTo:eof
GoTo:top

endlocal

Re: Alternative to CHOICE - need help

Posted: 03 Nov 2015 10:56
by foxidrive
Jer wrote:aGerman, your code works perfectly by prompting until a valid keystroke is pressed.
I am not at a skill level where I can understand much of it, so I'll have to stay within
what I can understand, or almost understand.


That latter bit is the wrong way to look at code.

Consider it this way - you can't understand all the code in Windows, yet you use it without considering the code.
Or you load a program from a third party author - and you know nothing about the code.




If it's a problem with integrating the routine - then explain the situation and ask how to add it.

Re: Alternative to CHOICE - need help

Posted: 03 Nov 2015 21:20
by Jer
foxdrive, my batch file projects and experiments are developed on a Windows 7 laptop.
When the batch code with CHOICE did not run on my Windows XP desktop PC, I looked
for an alternative to CHOICE. The code that I found on this forum is more in line with my
batch coding experience so far. The test for adapting others' more complex code to my
projects: “if it broke, could I fix it?” aGerman’s code is a puzzle to study. I'll only ask two
questions about the code:
set "n=0"
and…
set /a "n += 1"
and…
echo(!c!


Can you explain what is going on with these lines? Why are quotes used with n, a numeric variable?
What does the line with echo(!c! do?
Thanks.
Jer

Edit 11-6-2015: aGerman, your post of 11-4 gets a gold star. The choice function works flawlessly in my current batch project.
Thank you for the detailed explanation of the code.
Jerry
alternative to choice

Re: Alternative to CHOICE - need help

Posted: 04 Nov 2015 12:00
by aGerman

Code: Select all

:choice ByVal_Choices

The label is named choice. The rest of the line is only a hint that you have to call it with one argument that contains the allowed keys


Code: Select all

setlocal DisableDelayedExpansion

Begin of a sub-environment with delayed variable expansion disabled to ensure a proper assignment of values that may contain exclamation marks.


Code: Select all

set "n=0" &set "c=" &set "e=" &set "map=%~1"

n is the position of a character in the string of allowed keys. It is initialized with 0.
c is the read character from the end of the XCOPY output. Initially it is undefined.
e is a single character out of the string of allowed keys. Initially it is also undefined.
map is initialized with the first argument (string of allowed keys) that was passed.


Code: Select all

if not defined map endlocal &exit /b 0

If no parameter was passed return errorlevel 0 back to where the label was called.


Code: Select all

for /f "eol=1 delims=" %%i in ('xcopy /lwq "%~f0" :\') do set "c=%%i"

Assign the entire output of XCOPY to c.


Code: Select all

set "c=%c:~-1%"

Only take the last character which was the inputted key.


Code: Select all

if defined c (

If the Enter key was pressed c will be not defined at this position of the code. Since Enter can't be one of the allowed keys we skip the rest of the code in this case.


Code: Select all

  for /f delims^=^ eol^= %%i in ('cmd /von /u /c "echo(!map!"^|find /v ""^|findstr .') do (

Thats indeed a tricky line. It ECHOs the allowed keys in unicode mode which makes two bytes out of one character. The second byte is a "zero byte". If you pass such a string to FIND the "zero byte" will be interpreted as a line break.
So basically if the string in map was 123q this construct will separate the characters into four lines:
1
2
3
q
The for /F loop iterates linewise and reads the characters into %%i.


Code: Select all

    set /a "n += 1" &set "e=%%i"

n will be incremented by 1 at each iteration. That way it contains the line number of the separated characters.
e is one of the separated characters


Code: Select all

    setlocal EnableDelayedExpansion

Begin of a sub-environment with delayed variable expansion enabled to ensure a proper processing of values that may contain characters with special meaning (such as <>|&).


Code: Select all

    if /i "!e!"=="!c!" (
      echo(!c!
      for /f %%j in ("!n!") do endlocal &endlocal &exit /b %%j
    )

If the selected character is the same as the read character ...
output the read character ...
leave the sub-environments and return the position of the character in the string of allowed keys back to where the label was called.


Code: Select all

    endlocal
  )

Otherwise leave the "EnableDelayedExpansion" environment and continue with the next iteration.


Code: Select all

)
endlocal &goto choice

If there was no matched key then reset the environment and go back to the label and run the whole code again.

Re: Alternative to CHOICE - need help

Posted: 10 Nov 2015 03:06
by foxidrive
Jer, when people read a forum most of them will use the feature that shows them the latest posts.

You edited your previous post days afterward, and none of the regular users will see what you have added or changed - because the post is not added to the unread posts.

viewtopic.php?p=43810#p43810