Eli Richardson
Eli Richardson

Reputation: 930

Batch file "If" statement errors

I seem to be having tons of trouble making this program. I finally only have one more error. Here is my code:

:tutq
echo What is the first letter of your name?
echo 1(A
echo 2(B
echo 3(C
echo 4(D
echo 5(E
echo 6(F
echo 7(G
echo 8(H
echo 9(I
echo 10(J
echo 11(K
echo 12(L
echo 13(M
echo 14(N
echo 15(O
echo 16(P
echo 17(Q
echo 18(R
echo 19(S
echo 20(T
echo 21(U
echo 22(V
echo 23(W
echo 24(X
echo 25(Y
echo 26(Z
set /p tutnum=
If not defined !tutnum! (
cls
goto tutq
)
If "!tutnum!" == "1" (
set "tutlet=A"
goto tutp2
)
If "!tutnum!" == "2" (
set "tutlet=B"
goto tutp2
)
If "!tutnum!" == "3" (
set "tutlet=C"
goto tutp2
)
If "!tutnum!" == "4" (
set "tutlet=D"
goto tutp2
)
If "!tutnum!" == "5" (
set "tutnum=E"
goto tutp2
)
If "!tutnum!" == "6" (
set "tutlet=F"
goto tutp2
)
If "!tutnum!" == "7" (
set "tutlet=G"
goto tutp2
)
If "!tutnum!" == "8" (
set "tutlet=H"
goto tutp2
)
If "!tutnum!" == "9" (
set "tutlet=I"
goto tutp2
)
If "!tutnum!" == "10" (
set "tutlet=J"
goto tutp2
)
If "!tutnum!" == "11" (
set "tutlet=K"
goto tutp2
)
If "!tutnum!" == "12" (
set "tutlet=L"
goto tutp2
)
If "!tutnum!" == "13" (
set "tutlet=M"
goto tutp2
)
If "!tutnum!" == "14" (
set "tutlet=N"
goto tutp2
)
If "!tutnum!" == "15" (
set "tutlet=O"
goto tutp2
)
If "!tutnum!" == "16" (
set "tutlet=P"
goto tutp2
)
If "!tutnum!" == "17" (
set "tutlet=Q"
goto tutp2
)
If "!tutnum!" == "18" (
set "tutlet=R"
goto tutp2
)
If "!tutnum!" == "19" (
set "tutlet=S"
goto tutp2
)
If "!tutnum!" == "20" (
set "tutlet=T"
goto tutp2
)
If "!tutnum!" == "21" (
set "tutlet=U"
goto tutp2
)
If "!tutnum!" == "22"(
set "tutlet=V"
goto tutp2
)
If "!tutnum!" == "23" (
set "tutlet=W"
goto tutp2
)
If "!tutnum!" == "24" (
set "tutlet=X"
goto tutp2
)
If "!tutnum!" == "25" (
set "tutlet=Y"
goto tutp2
)
If "!tutnum!" == "26" (
set "tutlet=Z"
goto tutp2
)
:tutp2
echo Congrats!
echo If you did the exersize correct
 echo The first letter of your name should be "!tutlet!"
pause

Anyways if you could explain to me whats wrong.

Upvotes: 0

Views: 74

Answers (2)

Magoo
Magoo

Reputation: 80213

You've accepted a good answer - please leave it that way. This is supplemental information that may prove useful, if not to you then to others facing the same problem.

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "letequ=a(1 b(2 c(3 d(4 e(5"
:tutq
cls
FOR %%a IN (%letequ%) DO echo %%a
SET "tutnum="
SET /p "tutnum=What is the first letter of your name? "
IF NOT DEFINED tutnum GOTO tutq
FOR %%a IN (%letequ%) DO (
 SET letterandnumber=%%a
 SET "letter=!letterandnumber:~0,1!"
 SET "number=!letterandnumber:~2!"
 IF "!number!"=="%tutnum%" (
  ECHO The first letter of your name is "!letter!"&pause&GOTO :EOF)
)
GOTO tutq

So - I didn't bother to complete the letter/number set. It should be fairly obvious how to do that.

Note the technique of setting a string-value. That syntax ensures trailing spaces on a line are not included in the value assigned, which can sometimes cause problems. For instance,

SET tutnum=

should clear tutnum out of the environment which means that as far as cmd is concerned, it has a value of nothing but if there are stray (and conveniently invisible) spaces on the end of the line, it would be set to those spaces and if defined tutnum would be true.

SET "tutnum="

would clear tutnum as desired, stray spaces or no.

Setting a variable just before a set /p may seem superfluous, but if the response to a set /p is simply Enter then the variable will remain unchanged - it will not automatically have no value as may be assumed. This technique can be used to assign a default value

set "var=default"
set /p "var=prompt-string"

will leave default assigned to var if the user responds Enter

This could be used like

set "var=default"
set /p "var=prompt-string [%var%]"

To show the user the value that will be used if the user responds Enter

Next item to be examined is the for loops. for can be subject to all kinds of jiggery-pokery, but the basic for is

for %%x in (list of items separated by spaces) do something

where each item in the list in turn is assigned to %%a, so each item in the list leteq is assigned in turn to %%a and %%a adopts the values "a(1" "b(2" "c(3" "d(4" and "e(5", then does something with that value.

In the case of the analyse-the-entry portion of the code, something is

 SET letterandnumber=%%a
 SET "letter=!letterandnumber:~0,1!"
 SET "number=!letterandnumber:~2!"
 IF "!number!"=="%tutnum%" (
  ECHO The first letter of your name is "!letter!"&pause&GOTO :EOF)

The object here is to substring the value in %%a example: "c(3". We can't do a direct substring of %%a, so we assign %%a's value to letterandnumber and substring that.

The substring syntax is

%var:~m,n%
  • If m is >=0, then the start of the substring is the m'th character of var, starting at "character 0"
  • If m is <0, then the start of the substring is the m'th character from the end of var
  • If n is >0, then n is the length of the substring
  • If n is <0, then the end of the substring is the n'th character from the end of var
  • n may be omitted. If omitted, the substring is "from the start position to the end"
  • % may be ! for delayedexpansion mode

So letter is set to ~0,1 that is, the first character for a length of 1 of letterandnumber and similarly number is set to ~2 - the third letter up to the end of the string.

Since letterandnumber is set within the loop (or in any parenthesised sequence of statements, aka "a block") then it may appear as two separate values. %letterandnumber% refers to the value at the time the block is first encountered and parsed whereas !letterandnumber! refers to the value at run-time as it might be set within the block. The !var! syntax is only valid if a setlocal enabledelayedexpansion command has previously been executed.

The reason is that cmd replaces any %var% with the current value of var, then checks the statement for validity. !var! is not replaced until the block is executed.

The consequence of this sequence is significant and if not understood can lead to some vigorous head-scratching.

Suppose we have (note that close-parentheses ase used, not open)

SET "letequ=a)1 b)2 c)3"
FOR %%a IN (%letequ%) DO echo %%a

This will lead to a syntax error because cmd will first replace leteq with its then-current value and then parse it, so it sees

FOR %%a IN (a)1 b)2 c)3) DO echo %%a

assumes that the first close-parenthesis ends the list and expects a do but finds 1 so it reports the problem.

One way to solve this problem is to escape the misinterpreted ) by preceding it with a caret ^

SET "letequ=a^)1 b^)2 c^)3"
FOR %%a IN (%letequ%) DO echo %%a

cmd now sees FOR %%a IN (a^)1 b^)2 c^)3) DO echo %%a and recognises that ^) means "this parenthesis is data, not part of the command" It's therefore happy with the syntax and proceeds to execute the code.

Another way is this (if delayedexpansion is in effect)

SET "letequ=a)1 b)2 c)3"
FOR %%a IN (!letequ!) DO echo %%a

cmd does not substitute the value until after the command has passed the validity check so no error is raised.

Upvotes: 0

SomethingDark
SomethingDark

Reputation: 14370

Your code is actually doing exactly what you told it to, it's just going so fast you can't notice it.

When you are checking to see if a variable exists, don't wrap it in %s or !. if not defined varname essentially says, "if there is no variable called 'varname', then do this..."; when you surround it with % or !, you replace the variable with its value. If I entered 10 at the prompt, the code would be saying "If there is no variable called 10, then clear the screen and go back to tutq." Since there is no variable called 10, you're stuck in an infinite loop.

Change If not defined !tutnum! ( to If not defined tutnum ( and your code will work.

Upvotes: 1

Related Questions