For searches in Windows Registry using batch scripting, I have to loop through a few keys, make a comparison to determine which is the right one, and then update the key.
Iteration in a for loop seems impossible to break out of. I have seen that others are facing similar issues but there does not seem to be a simple solution. Here is a snippet that demonstrates the issue.
#echo off
echo.
echo Diet Favorites
set favorite="bananas"
for %%a in (apples, bananas, chocolates) do call :reviewList %%a
echo.
echo Processing completed.
goto end
:reviewList item
set foundFavorite="false"
call :chooseFavorite "%~1"
if /I "%foundFavorite%"=="true" (
echo found the favorite - %~1
exit /b 0
) else (
echo skip %~1
)
endlocal & goto :eof
:chooseFavorite item
if "%~1"==%favorite% set "foundFavorite=true"
endlocal & goto :eof
:end
chooseFavorite returns the favorite in an environment variable. The output shows that reviewList continues looping after the favorite is identified.
Diet Favorites
skip apples
found the favorite - bananas
skip chocolates
Processing completed.
The comparison is working, but if the exit worked as expected, chocolates diet option should not be listed at all. How do I neatly break out of the loop iteration?
#ECHO Off
SETLOCAL
echo.
echo Diet Favorites
set "favorite=bananas"
for %%a in (apples, bananas, chocolates) do call :reviewList %%a&IF DEFINED foundFavorite GOTO foundit
:foundit
echo.
echo Processing completed.
goto end
:reviewList param
set "foundFavorite="
call :chooseFavorite "%~1"
if DEFINED foundFavorite (
echo found the favorite - %~1
) else (
echo skip %~1
)
goto :eof
:chooseFavorite param
if "%~1"=="%favorite%" set "foundFavorite=%~1"
goto :eof
:end
GOTO :EOF
Since you don't use any setlocal commands in your posted code, the endlocals are superfluous.
This version sets foundFavorite to empty, and sets it to something (can be anything you like - I just chose the thing that was found, which is often convenient) when a match is found. if defined variable interprets the run-time status of the variable, so it can be used within a for loop.
BTW- try running your original code with echo ON. You would see that your if statements are actually executing if /i ""false""=="true" ...
This is why it's convention on SO to use the set "var=value" syntax for string-assignments. You can then apply quotes as required without worrying about where or whether to apply or remove quotes that may be in variables.
Check the return code of :reviewList in your loop, and break accordingly. You'll need to call properly exit /b 0 on non-breaker calls, and exit /b 1 (or any non-zero value) for breaker calls.
Try that:
#echo off
echo.
echo Diet Favorites
set favorite="bananas"
for %%a in (apples, bananas, chocolates) do (
call :reviewList %%a || goto :break_loop
)
:break_loop
echo.
echo Processing completed.
goto end
:reviewList item
set foundFavorite="false"
call :chooseFavorite "%~1"
if /I "%foundFavorite%"=="true" (
echo found the favorite - %~1
exit /b 1
) else (
echo skip %~1
)
endlocal
exit /b 0
:chooseFavorite item
if "%~1"==%favorite% set "foundFavorite=true"
endlocal & goto :eof
:end
Output is:
S:\Temp>test
Diet Favorites
skip apples
found the favorite - bananas
Processing completed.
Related
(This is my first post here, so bear with me)
Can you show the last user-input in a batch file? I'm gonna try to keep it simple here.
#echo off
:menu
echo Type 1 to proceed.
set /p example=
if "%example%" == "1" GOTO :proceed
GOTO :error
:proceed
pause
:error
cls
echo You wrote (last user input), that's not correct.
timeout 30
GOTO :menu
I know that I could replace the (last user input) with %example%, but then I'd have to make custom error messages for every category, and there are about 50 of them. It'd be easier with a last input command.
By the way, I've taught myself everything that I know about batch, so my example probably has major issues right now, but it works somehow.
You could centralize all user input into a function (user_input)
:menu1
echo Type 1 to proceed.
call :userInput example
if "%example%" == "1" GOTO :proceed
GOTO :error
:menu2
echo Type 42 to proceed.
call :userInput answer
if "%answer%" == "42" GOTO :proceed
GOTO :error
:userInput
set /p LAST_INPUT=
set "%1=%LAST_INPUT%"
exit /b
:proceed
pause
:error
cls
echo You wrote "%LAST_INPUT%", that's not correct.
timeout 30
GOTO :menu
I don't know how to do it without temp file. TO get the things written int the console you need the doskey /history (this will skip the running of the script itself):
#echo off
setlocal enableDelayedExpansion
set "last="
set "but_last="
doskey /history > log.txt
for /f "tokens=* delims=" %%# in (log.txt) do (
set "but_last=!last!"
set "last=%%#"
)
echo "%but_last%"
del /s /q log.txt >nul 2>nul
I've spent a few days trying to get this batch script to work, but it just does not seem to work properly. It seems to just do whatever it wants after it prompts me to set a variable and i set it.
For example, I might enter n when it says that it doesn't seem to exist, and it will just end the script like it should. But if I re-open it, and it says the same thing as before, and I enter n again, it might just jump to :DeleteCalc, as if I typed y.
Here's my script:
#echo off
:Begin
color fc
title My script
cls
if not exist "C:\calc.exe" (
echo calc.exe doesn't seem to exist. Attempt deletion anyway? ^(Y/N^)
set "calcnotexist="
set /p "calcnotexist="
::This command checks to see if the user inputs a quotation mark. If they do, it echos that quotes cannot be inputted.
setlocal EnableDelayedExpansion
if not !calcnotexist!==!calcnotexist:^"=! set "calcnotexist="
endlocal & if "%calcnotexist%"=="" (
echo ERROR - Quotes cannot be entered.
pause
goto Begin
)
if /i "%calcnotexist%"=="Y" (
echo.
goto DeleteCalc
)
if /i "%calcnotexist%"=="Yes" (
echo.
goto DeleteCalc
)
if /i "%calcnotexist%"=="N" goto End
if /i "%calcnotexist%"=="No" goto End
echo ERROR - Unrecognized input
pause
goto Begin
)
:calcDoesExist
title My script
cls
echo calc.exe found. Delete? ^(Y/N^)
set "calcexist="
set /p "calcexist="
::This command checks to see if the user inputs a quotation mark. If they do, it echos that quotes cannot be inputted.
setlocal enabledelayedexpansion
if not !calcexist!==!calcexist:^"=! set "calcexist="
endlocal & if "%calcexist%"=="" (
echo ERROR - Quotes cannot be entered.
pause
goto calcDoesExist
)
if /i "%calcexist%"=="Y" goto DeleteCalc
if /i "%calcexist%"=="Yes" goto DeleteCalc
if /i "%calcexist%"=="N" goto End
if /i "%calcexist%"=="No" goto End
echo ERROR - Unrecognized input
pause
goto calcDoesExist
:DeleteCalc
cls
echo Deleting...
if not exist C:\calc.exe goto Success
del /f /q C:\calc.exe >nul 2>nul
if not exist C:\calc.exe goto Success
echo Fail!
echo.
echo calc.exe could not be deleted.
echo.
pause
goto End
:Success
echo Deleted!
echo.
echo calc.exe successfully deleted.
echo.
pause
goto End
:End
exit /b
What could I possibly be doing wrong?
Thanks
P.S. I tested this by opening CMD and running the batch script multiple times in there. (but it also doesn't work right when just double clicking it)
If you restructure your script there will be no need for the extended If blocks and therefore no necessity to EnableDelayedExpansion. Also if you use Choice you will not have to do all of the verification of responses.
Example:
#Echo Off
Title My script
Color FC
:Begin
If Exist "C:\calc.exe" GoTo calcDoesExist
Echo(calc.exe doesn't seem to exist.
Choice /M "Attempt deletion anyway"
If ErrorLevel 3 (ClS & GoTo Begin)
If ErrorLevel 2 GoTo End
If ErrorLevel 1 GoTo Success
GoTo End
:calcDoesExist
Echo(calc.exe found.
Choice /M "Delete"
If ErrorLevel 3 (ClS & GoTo calcDoesExist)
If ErrorLevel 2 GoTo End
If ErrorLevel 1 GoTo DeleteCalc
GoTo End
:DeleteCalc
ClS
Echo(Deleting...
Del /A /F "C:\calc.exe">Nul 2>&1
If Not Exist "C:\calc.exe" GoTo Success
Echo(Fail!
Echo(
Echo(calc.exe could not be deleted.
GoTo End
:Success
Echo(
Echo(Deleted!
Echo(
Echo(calc.exe does not exist.
:End
Echo(
Echo(Exiting...
Timeout 3 /NoBreak>Nul
Exit /B
I can't fix this I want to be able to write anything as a response which works with
:being
but not
:Fap
here is my code
:begin
cls
echo You wake up and realize you are never going to amount to anything, oh well, might as well get on with your worthless life
echo fap or vidya games
echo.
set /p input=
if /i "%input%"=="Fap" goto Fap
if /i "%input%"=="Vidya games" goto vidya games
if /i "%input%"=="vidya" goto vidya games
if not "%input%"=="Fap"/"vidya"/"Vidya games" goto begin
:Fap
cls
echo Since you can't get a girl you decide to fantize about the girl of your dreams so you download some watamote doujins after a mixture of crying and masterbaiting you decide to change to mood
echo vidya or sleep
echo.
set /p input=
if /i "%input%"=="vidya" goto vidya fap
if /i "%input%"=="sleep" goto sleep
if not "%input%"=="vidya"/"sleep" goto Fap
Labels in batch scripts are marked by a colon like :begin. Same approach is used when redirecting to a certain label from IF, FOR or similar statement. So, edit your script by following this example:
if /i "%input%"=="Fap" (goto :Fap
) else if /i "%input%"=="Vidya" (goto :Vidya_games
) else (goto :begin)
Instead of those many ifs, you can check, if a label exists and if yes, jump to it:
If you have several questions with different destinations, you can name your labels accordingly:
#echo off
:loop
echo fap or vidya games
set /p "label=Command: "
findstr /b /i ":Q1-%label%" "%~f0" && goto :Q1-%label%
REM previous line checks, if the label exists and if yes, jumps to it
echo no such command: %label% & goto :loop
:Q1-fap
echo reached Question1 label one.
goto :eof
:Q1-vidya
echo reached Question1 label two.
goto :eof
Some notes:
labels are single word only. Everything after the first space is ignored.
findstr /i makes it insensitive to capitalization.
/b searches for lines that start with the string.
&& means "if previous command (findstr here) was successful, then"
%~f0 is the name of your batchfile.
This goes to Q1-vidya when you enter vidya, Vidya, VIDYa, vidya Games or Vidya anything.
I have a problem.
set /p command=
if %command% == "display (i want the second part of the variable here)" echo (second part of the variable)
For example, i type:
display hello
I want it to simply:
echo hello
I want to use this for custom commands in my game.
Firstly, you can split the text on firstword|notfirstword with a for /f loop using "tokens=1*". See help for in a console window for full details.
Next, you could use attempt to call :label where :label is whatever the first word was. In essence, you're creating batch functions and letting the user choose which function is executed. If the function label doesn't exist, then errorlevel will be non-zero and you can handle appropriately using conditional execution. This makes it easy to expand your script without having to add an if /i statement for each choice or synonym you add. (It might be a good idea to hide the error message for attempting to call a non-existent label by redirecting 2>NUL.) Here's a full example:
#echo off & setlocal
:entry
set /P "command=Command? "
for /f "tokens=1*" %%I in ("%command%") do (
2>NUL call :%%I %%J || (
if errorlevel 1000 (exit /b 0) else call :unsupported %%I
)
goto :entry
)
:display
:echo
:say
:show
echo(%*
exit /b 0
:ping
ping %~1
exit /b 0
:exit
:quit
:bye
:die
echo OK, toodles.
exit /b 1000
:unsupported <command>
1>&2 echo %~1: unrecognized command
exit /b 0
I found this tutorial to make a game on Notepad++ and make a batch file to run on CMD. Well here's what I have so far:
:ChooseWeapon
cls
echo "I almost forgot. Here are the weapons I have avaliable, choose one and begin your quest."
echo.
set /p weapon=What is your weapon? (Sword, Double-Bladed Axe, Dagger):
The point to this is to choose a weapon you wish to use by typing what you want. Now, the thing is I'm able to type "ghregff" and it would say that is my weapon. How do I make it so you have to choose either: Sword, Double-Bladed Axe, or Dagger?
You could make it a choice menu like below, and if they choose a number not 1 2 or 3, it will kick them back to enter a selection again.
#echo off
:chooseweapon
cls
echo "I almost forgot. Here are the weapons I have avaliable, choose one and begin your quest."
echo.
echo What is your weapon? (Sword, Double-Bladed Axe, Dagger):
echo 1 - Sword
echo 2 - Double-Bladed Axe
echo 3 - Dagger
echo.
set /P Weapon="Enter a choice: "
echo --------------------------------------------------------------------
for %%I in (1 2 3) do if #%Weapon%==#%%I goto wp%%I
goto chooseweapon
:wp1
Set weapon=Sword
goto end
:wp2
Set weapon=Double-Bladed Axe
goto end
:wp3
Set weapon=Dagger
goto end
:end
echo %weapon%
pause
#echo off
set "NumberWeapons=3"
:ChooseWeapon
color 07
cls
echo."I almost forgot. Here are the weapons I have avaliable, choose one and begin your quest."
echo.
echo.1 Sword
echo.2 Double-Bladed Axe
echo.3 Dagger
set /p Weapon=What is your weapon? (1-%NumberWeapons%):
echo.
if %Weapon% lss 1 goto :ChoiceError
IF %Weapon% gtr %NumberWeapons% goto :ChoiceError
GOTO :Start
:ChoiceError
COLOR CF
ECHO.Error! Choose a valid choice.
pause
goto :ChooseWeapon
:Start
echo.Replace this line with the rest of your stuff & pause
#ECHO OFF
SETLOCAL
SET setprompt=What is your weapon?
CALL :chooselist Sword, "Double-Bladed Axe", Dagger
ECHO(Choice made was %response%
CALL :chooselist2 Sword, "Double-Bladed Axe", Dagger
ECHO(Choice made was %response%
CALL :choosemenu Sword, "Double-Bladed Axe", Dagger
ECHO(Choice made was %response%
GOTO :EOF
:chooselist
SET valid=%*
SET "nchoices="
CALL :choose "%setprompt% (%valid:"=%):"
GOTO :eof
:chooselist2
SET valid=%*
SET "nchoices="
FOR %%Z IN (%*) DO ECHO %%~Z
CALL :choose "%setprompt%:"
GOTO :eof
:choosemenu
SET valid=%*
SET /a nchoices=0
FOR %%Z IN (%*) DO SET /a nchoices+=1&CALL ECHO %%nchoices%% : %%~Z
CALL :choose "%setprompt%:"
GOTO :eof
:choose
SET /p "response=%~1 "
IF NOT DEFINED response GOTO choose
IF DEFINED nchoices FOR /l %%Z IN (1,1,%nchoices%) DO IF "%%Z"=="%response%" CALL :setresp %valid%&GOTO :eof
SET /a cmatch=0
CALL :countresp %valid%
IF NOT %cmatch%==1 GOTO choose
GOTO :eof
:setresp
IF %response% neq 1 SET /a response-=1&shift&GOTO setresp
SET response=%~1
GOTO :eof
:countresp
SET $1=%~1
IF NOT DEFINED $1 (
IF %cmatch%==1 (SET response=%$3%)
GOTO :eof
)
CALL SET $2=%%$1:*%response%=%%
IF /i "%response%%$2%"=="%$1%" SET /a cmatch+=1&SET "$3=%~1"
shift&GOTO countresp
GOTO :eof
This is a flexible piece of code that allows you to make your choice in ne of three manners:
Using chooselist, the parameter-list is appended to setprompt and the user may choose to enter any response in full, or simply sufficient to unambiguously define the requirement. "s" would define Sword, for instance, but "do" would be required for "Double-Bladed Axe" and "da" for "Dagger" since "d" is ambiguous.
Using chooselist2 is similar, it simply lists the choices each on its separate line and asks for input.
Finally, choosemenu does the same, but numbers the line and the choice can be made using either the number or an unambiguous start-string.
The response will always be in response
The easiest way seems to be doing it through the findstr command.
:Choice
set /p weapon=What is your weapon? (Sword, Double-Bladed Axe, Dagger):
echo %weapon%| findstr /r "^Sword$ ^Double-Bladed Axe$ ^Dagger$">nul
if errorlevel 1 (
echo %weapon% was sadly not an option.
goto :Choice
)
echo you chose %weapon%.
(you need ^ and $. ^ means start of line, $ means end of line)
OR
if you want to use that system several times you could use findstr with parameters and a return value
:MakeChoice
set /p answer= :
echo %answer%| findstr /r "^%~2$ ^%~3$ ^%~4$ ^%~5$ ">nul
if errorlevel 1 (
echo "%answer%" is sadly not a valid choice, choose again.
goto :MakeChoice
)
set %~1=%answer%
goto :eof
You can than call the function this way:
echo choose a weapon(Sword, Axe, Dagger)
call :MakeChoice chosenOption "Sword" "Double-Bladed Axe" "Dagger"
echo you chose %chosenOption%
This works with 1-4 variables, but if you want more, you can always add ^%~6$ ^%~7$... after ^%~5$.