batch "While" loop doesn't work, but why? - loops

echo off
set /a a=0
:start
if %a% LEQ 21(
echo test
set /a a=%a%+1
goto start
)
I don't get it...
I only get a Syntax-error when the loop starts.

The code you posted isn't a valid bash script it's a batch script to echo test 20 times in a bash script using a for loop like this:
#/bin/bash
for i in {1..20}; do
echo test
done
Much clearer and readable than batch scripts, welcome to bash!

Besides the error already explained by Bali C, you should note that your code have a potential problem. In this command: set /a a=%a%+1 the %a% value is expanded just one time when the if command is executed. In this case your code run by chance because the goto command cause the if be executed again in each loop, but if your program would be larger and more complex, and this set command would be executed several times inside parentheses, the code would fail because %a% will be expanded just one time to the value that a variable had before enter the parentheses.
The way to solve this problem is easy in this case: just eliminate the percent signs, because set /a a=a+1 command can directly take the values of the variables. However, there is much more involved here! For further details, type set /? and pay attention to "delayed variable expansion" description.

You are missing a space between 21 and ( which will make it compare the number in the loop to 21(, also breaking the if statement, the latter being why you are getting a syntax error.
Add a space and it works fine.
echo off
set /a a=0
:start
if %a% LEQ 21 (
echo test
set /a a=%a%+1
goto start
)

Related

How to echo batch commands without executing?

I'm trying to make a self-building chatbot that learn questions by inputing question and three possible answers, then, it randomize that three answers and give it as output. The learned question goes to the final line of the same batch file.
Here's the sample:
#echo off&&setlocal enabledelayedexpansion
echo/teach me a question to answer.
set/p q=
echo(>>%~f0
echo/:%q: =%>>%~f0
cls
echo/teach me three ways to answer it.
set/p a1=
set/p a2=
set/p a3=
echo/set a[0]=%a1%>>%~f0
echo/set a[1]=%a2%>>%~f0
echo/set a[2]=%a3%>>%~f0
echo/"set/a ax=%random% %%3&&set aa=!a[%ax%]!">>%~f0
echo/"echo %aa%">>%~f0
But I get the following:
:howareyou?
a[0]=good
a[1]=fine
a[2]=well
"set/a ax=24793 %3&&set aa="
"echo "
It's possible to get
set/a ax=%random% %%3&&set aa=!a[%ax%]!
echo %aa%
exactly as it is?
There are two problems in this code.
First of all, to answer your question:
How to echo exactly what I type in?
In batch, the answer is always more complicated than you think. In case of echo, quotes don't really change the way % and ! interpreted, but they are echoed as well, which is bad now.
Instead, escape % by duplicating, & by putting a ^, and ! by putting two ^s before it [ref]:
echo set/a ax=%%random%% %%%% 3^&^&set aa=^^!a[%%ax%%]^^! >>%~f0
echo echo %%aa%% >>%~f0
Now you can see, that everything's echoed correctly, but the echoed code still won't work.
Why?
The second problem is the &&: percent-variables (%var%) will be substituted before the evaluation of the line or the command group (commands enclosed by parentheses).
That is, since the set /a and set are on the same line, %ax%'s value will be substituted before set /a sets it. That could be avoided by using the power of the Delayed Expansion, and its !ax! syntax, but in this case, it isn't possible because it is accessed inside another variable access.
So, instead do what && would do otherwise:
set/a ax=%random% %% 3
if %errorlevel% GTR 0 set aa=^^!a[%%ax%%]^^!
Finally, you end up with something like:
echo set/a ax=%%random%% %%%% 3 >>%~f0
echo if %%errorlevel%% GTR 0 set aa=^^!a[%%ax%%]^^! >>%~f0
echo echo %%aa%% >>%~f0
Take a look Here for a functional example of escaping exclamation marks used in delayed variables for this type of situation.
#echo off & setlocal DisableDelayedExpansion
Set "/Ex=^!"
and to utilize:
Echo(Set "Answer=%/Ex%Answer[%%RandAnswer%%]%/Ex%">>Bot.bat
Highly recommend reading here to understand the way the command line is parsed and why this works.

Batch "if" statement not working

I seem to have a problem with my "if" statements. Currently after the pause in "start" the file just closes and nothing else happens. Chances are, I have no idea what I'm doing.
#echo off
set startnum=0
goto start
:start
pause
set startnum="!startnum!"+1
if "%startnum%"="%0%" goto fail
if "%startnum%"="%1%" goto success
goto start
:success
cls
echo It worked!
echo "%startnum%"
pause.
exit
:fail
cls
echo Failure
pause.
exit
First problem:
set startnum="!startnum!"+1
Evidently, you wish to add 1 to startnum.
Your set command would set startnum to "!startnum!"+1. Literally. To perform arithmetic, you need set /a.
set /A startnum="!startnum!"+1
well, this won't work as "!startnum! isn't numeric. Had you invoked delayedexpansion beforehand, then the value of startnum would have been substituted for !startnum! yielding set /A startnum="0"+1 which makes more, but still not much sense.
set /A startnum=startnum+1
adds 1 to startnum - see set /? from the prompt for documentation.
set /A startnum+=1
would also add 1 to startnum.
Next problem.
if "%startnum%"="%0%" goto fail
Well, you appear to have found lss and all that gang. Problem is that the simple comparison operator is ==, not =.
if "%startnum%"=="%0%" goto fail
Now - what will that do? It will compare "thecontentsofstartnum" to "thecontentsof0". Since both of these arguments are quoted, batch will perform a string comparison. With a string comparison, 123 is less than 89 because 1 is less than 8.
But - you are attempting an equivalence comparison (equ as the operator may be used instead of ==) so the preceding point is simply AAMOI.
The difficulty is %0% which you may believe attempts to extract the value of the variable 0 but actually it replaces %0 with the value of the 0th parameter to the batchfile, which is the batchfile name itself, so you get "batchfilename%" - probably not what you actually wanted.
if "%startnum%"=="0" goto fail
is the way to implement that test.
The first IF statement is preprocessed by cmd.exe to
if ""!startnum!"+1"="test.bat" goto fail
which is a completely invalid IF condition.
cmd.exe outputs a syntax error message because of "="test.bat"" and exits batch file processing. This can be seen by debugging the batch file.
The solution is using right syntax for
assigning a value to an environment variable,
an arithmetic expression,
and last but not least the IF condition itself.
The batch file code fixed:
#echo off
set "startnum=0"
goto Begin
:Begin
set /A startnum+=1
if "%startnum%" == "0" goto Fail
if "%startnum%" == "1" goto Success
goto Begin
:Success
cls
echo It worked!
echo "%startnum%"
pause
exit /B
:Fail
cls
echo Failure
pause
exit /B
It would be safe here to remove all double quotes on both IF conditions.
One more hint: Don't use the name of a command like START as label. It works, but it should be avoided in case of ever adding to batch file the command START and searching for either command or label.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains how to reference batch file arguments.
cls /?
echo /?
exit /?
goto /?
if /?
pause /?
set /?
Further read the answers on following questions:
Batch file comparison of variable with constant fails
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
Where does GOTO :EOF return to?
Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files

Missing Operator Error In Batch Game

:teattack1
set /a health-=!monsterdmg! # It said missing operator here
pause
set /a monsterhealth-=!playerdmg! # And Here
pause
if "!monsterhealth!" == lss 0 goto tewin
pause
goto testencounter
pause
goto encountermenu
I keep getting Missing Operator
I serched how to fix it but did not find anything
You need to add:
Setlocal EnableDelayedExpansion
to your script to allow the use of !
Check here for an overview of what this does and why its needed
Open a command prompt window, enter set /? and read all output help pages carefully.
It is explained that when using set for an arithmetic expression, i.e. set /A, then variable names can be specified directly without expansion. Windows command processor interprets automatically each string not being a number or an operator as name of a variable and access the current variable value on evaluating the arithmetic expression.
Next run in command prompt window if /? and read again all output help pages carefully.
The line
if "!monsterhealth!" == lss 0 goto tewin
is definitely invalid as it contains the operator == and the operator lss which of course can't work.
This improved code is with all coding mistakes fixed.
:teattack1
set /a health-=monsterdmg
pause
set /a monsterhealth-=playerdmg
pause
if %monsterhealth% LEQ 0 goto tewin
pause
goto testencounter
pause
goto encountermenu
However, the environment variables health, monsterdmg, monsterhealth and playerdmg should all exist and have an integer value assigned as otherwise Windows command interpreter uses value 0 for each variable making the code not really useful.

How to abstract a given variable from a integerd number in CMD?

What's the syntax of the command that could abstract a variable from a basic, integered number?
For example
by executing a batch file, a variable (like a flag) is automaticaly set to 0, grows by 1 each time another specific command is executed and peaks at 10.
I want to know at any given point, how many times this flag-variable has grown, so I could get the exact times that the affiliated command has been executed and most importantly how many times this variable still has to grow.
So, thinking purely mathematicaly, I thought of abstracting 10 out of the variable to get my answers. But i can't make this to work on CMD.
I used this exact line in the code :
set /a 10-variable & ( echo executions of that command left )
But this only ends up showing executions of that command left without showing any number what so ever.
You have a logical quirk. set doesn't work the way you do it.
#echo off
set /a variable=0
:loop
set /a variable+=1
set /a left=10-%variable%
echo %left% executions left.
if %left% gtr 0 goto :loop
echo done.
Alternative (using only one variable instead of two):
set /a left=10
:loop2
set /a left-=1
echo %left% executions left.
if %left% gtr 0 goto :loop2
echo done.
Note: set /a var+=1 is just a short form of set /a var=%var%+1

Batch: Is there a way to set it up so that if at any point in time any variable equals something it does a command?

Basically what my title says.
Is there a way to set it up so that if at any point in time any variable equals something it does a command?
Without knowing what you hope to accomplish, it's difficult to know what will work for you. However, I have thought of a potential solution. For the variable you want to watch, instead of simply calling set "varname=value", you could call :set varname value, then use a subroutine to perform the watching. Here's an example:
#echo off
setlocal
set /a foo = 10, loop = 1
:loop
call :set foo "%foo% * %loop%"
set /a loop += 1
goto loop
:: End main script / begin :set subroutine
:set <var_to_set> <expression>
setlocal
set /a test = %~2
:: watch for %foo% to have a value greater than 50000
if %test% geq 50000 (
echo %~1 equals %test%. Press any key to exit.
>NUL pause
exit
)
endlocal & set "%~1=%test%"
goto :EOF
Of course this is much less efficient than a for /L loop, but it should demonstrate the proof of concept. The :set subroutine effectively watches %foo% to reach a value, then performs an action.
See this page on endlocal and this page on DOS functions for more examples of using subroutines to set variable values.

Resources