Batch: How to Properly Use CHOICE Inside of CALL Function? - windows

I have a rather confusing problem when using nested CALL functions and CHOICE commands inside of a batch file.
To summarize in pseudo code:
1.) Use a CHOICE command, which uses CALL :function1 when the correct option is selected
2.) :function1 uses CALL :setVar_n
3.) :setVar_n sets a list of variables, and ends with EXIT /B to return to :function1
4.) :function1 has a CHOICE command (Y/N), where Y will continue to perform operations then end with EXIT /B, and N ends with EXIT /B immediately
The issue:
The CHOICE command in :function1 always evaluates to N (the second option) regardless of input.
I don't understand why using %ERRORLEVEL% fails, while IF ERRORLEVEL works fine. I am also unsure why the use of CALL causes %ERRORLEVEL% to stop working in the first place.
I'm trying to avoid rewriting every choice command (There must be at least 50, some with 25+ options).
When it's written using %ERRORLEVEL% it fails:
::Return from setVar_n here
CHOICE /C YN /M "Continue? Y/N >"
IF %ERRORLEVEL%==2 (EXIT /B)
::function1 continues here
If I use IF ERRORLEVEL:
::Return from setVar_n here
CHOICE /C YN /M "Continue? Y/N >"
IF ERRORLEVEL 2 (EXIT /B)
::function1 continues here
It works properly. The issue is that the CHOICE problem persists even after :function1 ends. It affects all CHOICE commands in the entire file, so %ERRORLEVEL% cannot be used at all.
Can anyone shed some light on this issue?
Here's a full file code to test with, which might make more sense:
#ECHO OFF
:start
choice /c ABC
if %errorlevel%==1 (goto start)
if %errorlevel%==2 (call :function1)
if %errorlevel%==3 (goto start)
echo Function 1 completed
pause
choice /c ABC
if %errorlevel%==1 (echo 1)
if %errorlevel%==2 (echo 2)
if %errorlevel%==3 (echo 3)
pause
exit
:setVar
set /a var1=2
set /a var2=3
exit /b
:function1
echo In Function 1
call :setVar
choice /c YN /m "Continue (Y) or Finish (N)"
if %errorlevel%==2 (exit /b)
echo Still inside function 1
exit /b

Thanks to Stephan for the correct answer in the comments.
The solution is to use
set "errorlevel="
Before the choice command.

Related

Windows batch if being selected on wrong value

I'm trying to use the CHOICE command in a little test batch file.
Here is the code I have:
#ECHO Off
choice /M "Is this correct"
IF ERRORLEVEL 1 echo This is correct
IF ERRORLEVEL 2 echo This is not correct
echo %errorlevel%
When I press y I get This is correct but when I press n I get This is correct and This is not correct
Why is the first option being triggered? From the echo %errorlevel% I can see the errorlevel is 2.
I am using echo here as an example, it the actual batch file I have a goto and I am getting the first goto triggered all the time.
Found on ss64
IF ERRORLEVEL n statements should be read as IF Errorlevel >= number
Meaning that it will execute all other functions that are less than or equal to ERRORLEVEL.
To avoid that, try this.
#Echo off
choice /m "Would you like to continue?"
if %ERRORLEVEL%==1 Echo Response was Y
if %ERRORLEVEL%==2 Echo Response was N
Echo %ERRORLEVEL%
pause
The %'s surrounding the ERRORLEVEL will display the value of the variable.

How to have a called batch file halt the parent [duplicate]

I have a simple function written to check for directories:
:direxist
if not exist %~1 (
echo %~1 could not be found, check to make sure your location is correct.
goto:end
) else (
echo %~1 is a real directory
goto:eof
)
:end is written as
:end
endlocal
I don't understand why the program would not stop after goto:end has been called. I have another function that uses the same method to stop the program and it work fine.
:PRINT_USAGE
echo Usage:
echo ------
echo <file usage information>
goto:end
In this instance, the program is stopped after calling :end; why would this not work in :direxist? Thank you for your help!
I suppose you are mixing call and goto statements here.
A label in a batch file can be used with a call or a goto, but the behaviour is different.
If you call such a function it will return when the function reached the end of the file or an explicit exit /b or goto :eof (like your goto :end).
Therefore you can't cancel your batch if you use a label as a function.
However, goto to a label, will not return to the caller.
Using a synatx error:
But there is also a way to exit the batch from a function.
You can create a syntax error, this forces the batch to stop.
But it has the side effect, that the local (setlocal) variables will not be removed.
#echo off
call :label hello
call :label stop
echo Never returns
exit /b
:label
echo %1
if "%1"=="stop" goto :halt
exit /b
:halt
call :haltHelper 2> nul
:haltHelper
()
exit /b
Using CTRL-C:
Creating an errorcode similar to the CTRL-C errorcode stops also the batch processing.
After the exit, the setlocal state is clean!
See #dbenham's answer Exit batch script from inside a function
Using advanced exception handling:
This is the most powerful solutions, as it's able to remove an arbitrary amount of stack levels, it can be used to exit only the current batch file and also to show the stack trace.
It uses the fact, that (goto), without arguments, removes one element from the stack.
See Does Windows batch support exception handling?
jeb's solution works great. But it may not be appropriate in all circumstances. It has 2 potential drawbacks:
1) The syntax error will halt all batch processing. So if a batch script called your script, and your script is halted with the syntax error, then control is not returned to the caller. That might be bad.
2) Normally there is an implicit ENDLOCAL for every SETLOCAL when batch processing terminates. But the fatal syntax error terminates batch processing without the implicit ENDLOCAL! This can have nasty consequences :-( See my DosTips post SETLOCAL continues after batch termination! for more information.
Update 2015-03-20 See https://stackoverflow.com/a/25474648/1012053 for a clean way to immediately terminate all batch processing.
The other way to halt a batch file within a function is to use the EXIT command, which will exit the command shell entirely. But a little creative use of CMD can make it useful for solving the problem.
#echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
exit /b
:main
call :label hello
call :label stop
echo Never returns
exit /b
:label
echo %1
if "%1"=="stop" exit
exit /b
I've got both my version named "daveExit.bat" and jeb's version named "jebExit.bat" on my PC.
I then test them using this batch script
#echo off
echo before calling %1
call %1
echo returned from %1
And here are the results
>test jebExit
before calling jebExit
hello
stop
>test daveExit
before calling daveExit
hello
stop
returned from daveExit
>
One potential disadvantage of the EXIT solution is that changes to the environment are not preserved. That can be partially solved by writing the environent to a temporary file before exiting, and then reading it back in.
#echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
for /f "eol== delims=" %%A in (env.tmp) do set %%A
del env.tmp
exit /b
:main
call :label hello
set junk=saved
call :label stop
echo Never returns
exit /b
:label
echo %1
if "%1"=="stop" goto :saveEnvAndExit
exit /b
:saveEnvAndExit
set >env.tmp
exit
But variables with newline character (0x0A) in the value will not be preserved properly.
If you use exit /b X to exit from the function then it will set ERRORLEVEL to the value of X. You can then use the || conditional processing symbol to execute a command if ERRORLEVEL is non zero.
#echo off
setlocal
call :myfunction PASS || goto :eof
call :myfunction FAIL || goto :eof
echo Execution never gets here
goto :eof
:myfunction
if "%1"=="FAIL" (
echo myfunction: got a FAIL. Will exit.
exit /b 1
)
echo myfunction: Everything is good.
exit /b 0
Output from this script is:
myfunction: Everything is good.
myfunction: got a FAIL. Will exit.
Here's my solution that will support nested routines if all are checked for errorlevel
I add the test for errolevel at all my calls (internal or external)
#echo off
call :error message&if errorlevel 1 exit /b %errorlevel%<
#echo continuing
exit /b 0
:error
#echo in %0
#echo message: %1
set yes=
set /p yes=[no]^|yes to continue
if /i "%yes%" == "yes" exit /b 0
exit /b 1

I am using a Batch File to search a list of functions for an exact match, wondering if its possible to do a keyword search instead

I am using a Batch File to search a list of functions for an exact match, wondering if its possible to do a key word search, so right now i have to type "open cmd"
(variations on spacing and capital letters are accounted for)
Id like to switch it over to a system that can look for "cmd" and perform the action so "hey, open cmd please" would yield the same result as the old system
Old system:
setlocal
:: /STARTUP
set speech=start scripts\nircmd.exe speak text
cls
:begin
set TALK=TypeSomething
SET /P TALK=
set TALK=%TALK:?=%
call :%TALK: =% 2>NUL
if %errorlevel% equ 0 goto begin
exit /B 0
:unknown
echo Old function no longer supported
:opencmd
:BOSopencmd
:cmd
echo Command Prompt has now been opened in a new window, sir.
%speech% "Command Prompt has now been opened in a new window, sir."
start scripts\cmd.bat
exit /B 0
It is based of a chat bot i tried to make in middle school so the %speech% is not an important item, i can add that and the echo later. I just need a system that works like the old one if possible. The other i can have any number of functions with
:cmd
start cmd
Exit /B 0
or
:reddit
start http://www.reddit.com/
exit /B 0
at these need to be able to stack. I can transition to having scripts for each function in a separate batch files if needed. Ive tried trying findstr but it wasn't giving the desired results. Ive exhausted my knowledge on what i might be able to do but I've come up short lol, If you are having trouble understanding what i'm asking don't hesitate to let me know
I learn by taking things apart so partial code is appreciated but will not be much help until after I've figured out what does what .
Here's a sample of how you might approach it using ECHO, FINDSTR, and CALL (This is a modified example from the original per your request to be able to process multiple keywords):
#echo off
set TST_FNDFLG=FALSE
set TST_USRANS=
set /P TST_USRANS=Enter keywords:
if "%TST_USRANS%" == "" goto ENDIT
echo %TST_USRANS% | findstr /i "CMD" >NUL 2>&1
if ERRORLEVEL 1 goto TRYRED
call :DOCMD
:TRYRED
echo %TST_USRANS% | findstr /i "REDDIT" >NUL 2>&1
if ERRORLEVEL 1 goto TRYGOO
call :DORED
:TRYGOO
echo %TST_USRANS% | findstr /i "GOOGLE" >NUL 2>&1
if ERRORLEVEL 1 goto TRYEND
call :DOGOO
goto TRYEND
:DOCMD
if [%TST_FNDFLG%] == [FALSE] echo.
echo CMD was found in "%TST_USRANS%"
set TST_FNDFLG=TRUE
goto :EOF
:DORED
if [%TST_FNDFLG%] == [FALSE] echo.
echo REDDIT was found in "%TST_USRANS%"
set TST_FNDFLG=TRUE
goto :EOF
:DOGOO
if [%TST_FNDFLG%] == [FALSE] echo.
echo GOOGLE was found in "%TST_USRANS%"
set TST_FNDFLG=TRUE
goto :EOF
:TRYEND
echo.
if [%TST_FNDFLG%] == [TRUE] echo No more keywords found
if [%TST_FNDFLG%] == [FALSE] echo Did not find any known keywords
goto ENDIT
:ENDIT
echo.
set TST_USRANS=
set TST_FNDFLG=

Using a Batch File to make simple Y/N questions. Problematic Code. HALP

This is the code I am having issues with. I need to execute the choice lines properly by sending to their respected classes (cls). All of the errors are with the ERRORLEVEL and choice scripts. This is the only main issue I have but if you can help by pointing out others then I would be thankful. Best wishes, Mystikality.
cls
#echo off
echo Welcome to the Alpha PPFI Program V0.001.
GOTO T1
:T1
echo This is a security program ran just by a simple Batch file.
GOTO T2
:T2
echo If you have an interest in our program, enter your response below.
GOTO Variable
:Variable
CHOICE /C YN /N /M "Welcome, if you would wish to continue, enter Y/N
below."
if %errorlevel%==Y GOTO Create
if %errorlevel%==N GOTO ExitWarning
:Create
CHOICE /C YN /N /M "Create a new PPFI interface? Y/N"
if %errorlevel%==Y GOTO BatchGen
if %errorlevel%==N GOTO Variable
:BatchGen
start call CreatePPFI_V0.001.bat
exit
pause
:ExitWarning
CHOICE /C YN /N /M "Are you sure you want to exit V0.001 PPFI interface
alpha? Y/N"
if %errorlevel%==Y GOTO Variable
if %errorlevel%==N GOTO exit'
You need to check errorlevel to be 1 (first choice, "Y") or 2 (second choice, "N")
For example
if %errorlevel%==1 GOTO Create
if %errorlevel%==2 GOTO ExitWarning
You need to check errorlevel as other poster says. However their method won't work. If errorlevel tests for that number OR HIGHER. So reverse their lines or do it in a position independent way.
if errorlevel=1 if not errorlevel=2 Echo Error level is 1 exactly
See choice /? and if /? for help.

CMD Script Errorlevel Misbehavior with IF statement

Expected outcome of the following script: PERMPING if user presses P, or PINGLOOP if user presses T. However, no matter what the user presses, the script echos both. Any idea what gives?
CHOICE /C:PT /N /M "Ping permanently (P) or temporarily (T) (%pingTimes% times)?"
echo %ERRORLEVEL%
IF ERRORLEVEL 1 ECHO PERMPING
IF ERRORLEVEL 2 ECHO PINGLOOP
I suspect you are not acurately reporting the results of your code. The code you have posted should print both if P is pressed, and only print PINGLOOP if T is pressed. The behavior is due to how the IF ERRORLEVEL statement works, as is explained by the help. To get help on any command, simply type HELP commandName or commandName /? from a command prompt. In your case, you could use IF /? to see the following
Performs conditional processing in batch programs.
IF [NOT] ERRORLEVEL number command
...
ERRORLEVEL number Specifies a true condition if the last program run
returned an exit code equal to or greater than the number
specified.
...
You have 2 choices to make your code work:
Test the conditions in decreasing numerical order and use the ELSE clause
CHOICE /C:PT /N /M "Ping permanently (P) or temporarily (T) (%pingTimes% times)?"
echo %ERRORLEVEL%
IF ERRORLEVEL 2 (
ECHO PINGLOOP
) ELSE IF ERRORLEVEL 1 (
ECHO PERMPING
)
or use IF %ERRORLEVEL%==N
CHOICE /C:PT /N /M "Ping permanently (P) or temporarily (T) (%pingTimes% times)?"
echo %ERRORLEVEL%
IF %ERRORLEVEL% == 1 ECHO PERMPING
IF %ERRORLEVEL% == 2 ECHO PINGLOOP
To expand on the answer above if command extensions are turned on you can also use:
if %errorlevel% equ 1 echo PERMPING
if %errorlevel% equ 2 echo PINGLOOP

Resources