Batch file "If" statement errors - windows

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.

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.

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.

Related

Verify that user input is one of several allowed words

So I am having trouble with this line of code, which is meant to check if user input matches the given options.
set /p "myVar=---> "
echo %myVar%|findstr /ix "red:Red:blue:Blue">nul && (
echo %myVar% matched
) || (
echo %myVar% not matched
)
Is there a way I can go it like the above or any other way?
You could make use of the fact that for /F returns an exit code of 1 in case of zero iterations:
set /P VAR="Enter something: " || ((echo Empty input!) & exit /B)
(for /F "delims=AaEeIiOoUuYy eol=y" %%K in ("%VAR%") do rem/) && (
echo No match found!
) || (
echo Match encountered.
)
If the input consists only of characters listed after delims=, the for /F loop does not iterate and returns an exit code of 1; the command behind && only executes in case the exit code is 0, the command behind || only executes in case the exit code is not 0.
If no input is provided, set /P sets the exit code to 1.
Here is a basic example of what you want to do. I have not tested in DOS since I do not have any VM (or very old computer) right here, but IIRC this should work in plain DOS as well.
#echo off
if %var% == A GOTO :anything
if %var% == E GOTO :anything
if %var% == I GOTO :anything
if %var% == O GOTO :anything
if %var% == U GOTO :anything
if %var% == Y GOTO :anything
goto end
:anything
echo anything
:end
I think, choice is your best friend here. But if you want to do it without choice (it isn't available in all Windows versions):
set /p "myVar=---> "
echo %myVar%|findstr /ix "a e i o u y">nul && (
echo %myVar% matched
) || (
echo %myVar% not matched
)
It asks for input and checks if it is exactly one of the characters from the list, ignoring capitalization.

How do i check if input is any integer?

Simply asked, I need to check if a variable is numerical. I'm aware of the ability of:
set /a variable1=%variable%
setting non numerical strings to 0, but i need to be able to have 0 as an intiger as well as negative numbers.
This will be run very often, so a fast script is preferred. I've tried to echo the variable into a .txt, and use a for loop to scan through and return an error if anything other than 0-9 is detected, but the script is excessively long running, and frankly is a mess.
You could do something to this affect. Remove all numbers. If anything is left over it is not an integer. Not saying this is perfect but it is a step in the right direction.
set "tempvar="
FOR /F "tokens=* delims=-0123456789" %%G IN ("%variable1%") DO SET "tempvar=%%G"
IF DEFINED tempvar echo NOT AN INTEGER
As mentioned in question17584282
The easiest for digits should be:
IF %1 NEQ +%1 echo Notnumeric!
If negative numbers (hyphen) are also to be considered, this will work
SET number=%1
if %1 EQU +%1 echo positive number
if %1==-%number:-=% echo negative number
Learned from https://www.itprotoday.com/compute-engines/jsi-tip-9692-how-can-batch-script-determine-if-variable-or-parameter-integer
#echo off
:isInterer input [returnVar]
setlocal enableDelayedexpansion
set "input=%~1"
if "!input:~0,1!" equ "-" (
set "input=!input:~1!"
) else (
if "!input:~0,1!" equ "+" set "input=!input:~1!"
)
for %%# in (1 2 3 4 5 6 7 8 9 0) do (
if not "!input!" == "" (
set "input=!input:%%#=!"
)
)
if "!input!" equ "" (
set result=true
) else (
set result=false
)
endlocal & if "%~2" neq "" (set %~2=%result%) else echo %result%
try this.Some special symbols like ! and ^ could cause trouble though.
You can also use findstr:
#echo off
:isIntererFindstr input [returnVar]
setlocal enableDelayedexpansion
set "input=%~1"
if "!input:~0,1!" equ "-" (
set "input=!input:~1!"
) else (
if "!input:~0,1!" equ "+" set "input=!input:~1!"
)
echo !input!|findstr /r "[^0-9]" >nul 2>&1
if %errorlevel% equ 0 (
set result=false
) else (
set result=true
)
endlocal & if "%~2" neq "" (set %~2=%result%) else echo %result%

Windows batch if-else not working

[simple program that recieves an integer as input and prints if that number is trivial or not]
when i run this i get an error "( was unexpected at this time"
#echo off
set /a i=2
set /p input="enter an integer: "
set /a n=input
set /a t=n/2
:loop1
if %t% LSS %i% (
goto trivial
) else (
set /a t0=n%i
if %t0%==0 (
goto notTrivial
) else (
set /a i=i+1
goto loop1
)
)
:trivial
echo %n% is trivial
goto endd
:notTrivial
echo %n% is not trivial
:endd
pause > nul
but when I remove else statement in loop1 (which is btw unnecessary (because of goto command in if block)) it works
:loop1
if %t% LSS %i% (
goto trivial
)
set /a t0=n%i
if %t0%==0 (
goto notTrivial
) else (
set /a i=i+1
goto loop1
)
(how) is this possible?
When you remove the else clause, the code inside it is now out of any block.
Why does it matter? Because in batch files, lines or blocks of lines (code inside parenthesis) are first parsed and then executed. While parsed variable read operations are removed, being replaced with the value inside the variable at parse time, before starting to execute the command (more here).
So, in this code
) else (
set /a t0=n%i
if %t0%==0 (
goto notTrivial
) else (
set /a i=i+1
goto loop1
)
)
you change the value of the variable t0, but you can not retrieve this changed value inside the same block. But if you remove the else clause the code is not inside a block and everything works as intended (except syntax errors, try with set /a "t0=n %% i").
Firstly, you need to state the modulo operator % as %% in batch files.
Secondly, just move the command set /a t0=n%%i up before the if block begins, then it will work:
:loop1
set /a t0=n%%i
if %t% LSS %i% (
goto trivial
) else (
if %t0% EQU 0 (
goto notTrivial
) else (
set /a i+=1
goto loop1
)
)
So the change of variable t0 is moved outside of a command block ().
Alternatively, you could also enable delayed expansion:
setlocal EnableDelayedExpansion
rem INITIAL CODE PORTION...
:loop1
if %t% LSS %i% (
goto trivial
) else (
set /a t0=n%%i
if !t0! EQU 0 (
goto notTrivial
) else (
set /a i+=1
goto loop1
)
)
rem REMAINING CODE PORTION...
endlocal
You will notice the !t0! type expansion which, in contrast to %t0%, will expand t0 at execution time rather than parse time.
See also setlocal /? and endlocal /? for more information about these commands.

IF... OR IF... in a windows batch file

Is there a way to write an IF OR IF conditional statement in a windows batch-file?
For example:
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
The zmbq solution is good, but cannot be used in all situations, such as inside a block of code like a FOR DO(...) loop.
An alternative is to use an indicator variable. Initialize it to be undefined, and then define it only if any one of the OR conditions is true. Then use IF DEFINED as a final test - no need to use delayed expansion.
FOR ..... DO (
set "TRUE="
IF cond1 set TRUE=1
IF cond2 set TRUE=1
IF defined TRUE (
...
) else (
...
)
)
You could add the ELSE IF logic that arasmussen uses on the grounds that it might perform a wee bit faster if the 1st condition is true, but I never bother.
Addendum - This is a duplicate question with nearly identical answers to Using an OR in an IF statement WinXP Batch Script
Final addendum - I almost forgot my favorite technique to test if a variable is any one of a list of case insensitive values. Initialize a test variable containing a delimitted list of acceptable values, and then use search and replace to test if your variable is within the list. This is very fast and uses minimal code for an arbitrarily long list. It does require delayed expansion (or else the CALL %%VAR%% trick). Also the test is CASE INSENSITIVE.
set "TEST=;val1;val2;val3;val4;val5;"
if "!TEST:;%VAR%;=!" neq "!TEST!" (echo true) else (echo false)
The above can fail if VAR contains =, so the test is not fool-proof.
If doing the test within a block where delayed expansion is needed to access current value of VAR then
for ... do (
set "TEST=;val1;val2;val3;val4;val5;"
for /f %%A in (";!VAR!;") do if "!TEST:%%A=!" neq "!TEST!" (echo true) else (echo false)
)
FOR options like "delims=" might be needed depending on expected values within VAR
The above strategy can be made reliable even with = in VAR by adding a bit more code.
set "TEST=;val1;val2;val3;val4;val5;"
if "!TEST:;%VAR%;=!" neq "!TEST!" if "!TEST:;%VAR%;=;%VAR%;"=="!TEST!" echo true
But now we have lost the ability of providing an ELSE clause unless we add an indicator variable. The code has begun to look a bit "ugly", but I think it is the best performing reliable method for testing if VAR is any one of an arbitrary number of case-insensitive options.
Finally there is a simpler version that I think is slightly slower because it must perform one IF for each value. Aacini provided this solution in a comment to the accepted answer in the before mentioned link
for %%A in ("val1" "val2" "val3" "val4" "val5") do if "%VAR%"==%%A echo true
The list of values cannot include the * or ? characters, and the values and %VAR% should not contain quotes. Quotes lead to problems if the %VAR% also contains spaces or special characters like ^, & etc. One other limitation with this solution is it does not provide the option for an ELSE clause unless you add an indicator variable. Advantages are it can be case sensitive or insensitive depending on presence or absence of IF /I option.
I don't think so. Just use two IFs and GOTO the same label:
IF cond1 GOTO foundit
IF cond2 GOTO foundit
ECHO Didn't find it
GOTO end
:foundit
ECHO Found it!
:end
A simple "FOR" can be used in a single line to use an "or" condition:
FOR %%a in (item1 item2 ...) DO IF {condition_involving_%%a} {execute_command}
Applied to your case:
FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE
Suppress executing twice
A comment pointed out that {execute_command} may be encountered twice. To avoid this, you can use a goto after the first encounter.
FOR %%a in (1 2) DO IF %var%==%%a (
ECHO TRUE
goto :continue
)
:continue
If you think there's a possibility that {execute_command} might be executed twice and you don't want that, you can just add && goto :eof:
FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE && goto :eof
Much simpler, and still on a single line.
Thanks for this post, it helped me a lot.
Dunno if it can help but I had the issue and thanks to you I found what I think is another way to solve it based on this boolean equivalence:
"A or B" is the same as "not(not A and not B)"
Thus:
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
Becomes:
IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
Even if this question is a little older:
If you want to use if cond1 or cond 2 - you should not use complicated loops or stuff like that.
Simple provide both ifs after each other combined with goto - that's an implicit or.
//thats an implicit IF cond1 OR cond2 OR cond3
if cond1 GOTO doit
if cond2 GOTO doit
if cond3 GOTO doit
//thats our else.
GOTO end
:doit
echo "doing it"
:end
Without goto but an "inplace" action, you might execute the action 3 times, if ALL conditions are matching.
There is no IF <arg> OR or ELIF or ELSE IF in Batch, however...
Try nesting the other IF's inside the ELSE of the previous IF.
IF <arg> (
....
) ELSE (
IF <arg> (
......
) ELSE (
IF <arg> (
....
) ELSE (
)
)
The goal can be achieved by using IFs indirectly.
Below is an example of a complex expression that can be written quite concisely and logically in a CMD batch, without incoherent labels and GOTOs.
Code blocks between () brackets are handled by CMD as a (pathetic) kind of subshell. Whatever exit code comes out of a block will be used to determine the true/false value the block plays in a larger boolean expression. Arbitrarily large boolean expressions can be built with these code blocks.
Simple example
Each block is resolved to true (i.e. ERRORLEVEL = 0 after the last statement in the block has executed) / false, until the value of the whole expression has been determined or control jumps out (e.g. via GOTO):
((DIR c:\xsgdde /w) || (DIR c:\ /w)) && (ECHO -=BINGO=-)
Complex example
This solves the problem raised initially. Multiple statements are possible in each block but in the || || || expression it's preferable to be concise so that it's as readable as possible. ^ is an escape char in CMD batches and when placed at the end of a line it will escape the EOL and instruct CMD to continue reading the current batch of statements on the next line.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
(
(CALL :ProcedureType1 a b) ^
|| (CALL :ProcedureType2 sgd) ^
|| (CALL :ProcedureType1 c c)
) ^
&& (
ECHO -=BINGO=-
GOTO :EOF
)
ECHO -=no bingo for you=-
GOTO :EOF
:ProcedureType1
IF "%~1" == "%~2" (EXIT /B 0) ELSE (EXIT /B 1)
GOTO :EOF (this line is decorative as it's never reached)
:ProcedureType2
ECHO :ax:xa:xx:aa:|FINDSTR /I /L /C:":%~1:">nul
GOTO :EOF
It's possible to use a function, which evaluates the OR logic and returns a single value.
#echo off
set var1=3
set var2=5
call :logic_or orResult "'%var1%'=='4'" "'%var2%'=='5'"
if %orResult%==1 (
echo At least one expression is true
) ELSE echo All expressions are false
exit /b
:logic_or <resultVar> expression1 [[expr2] ... expr-n]
SETLOCAL
set "logic_or.result=0"
set "logic_or.resultVar=%~1"
:logic_or_loop
if "%~2"=="" goto :logic_or_end
if %~2 set "logic_or.result=1"
SHIFT
goto :logic_or_loop
:logic_or_end
(
ENDLOCAL
set "%logic_or.resultVar%=%logic_or.result%"
exit /b
)
If %x%==1 (
If %y%==1 (
:: both are equal to 1.
)
)
That's for checking if multiple variables equal value. Here's for either variable.
If %x%==1 (
:: true
)
If %x%==0 (
If %y%==1 (
:: true
)
)
If %x%==0 (
If %y%==0 (
:: False
)
)
I just thought of that off the top if my head. I could compact it more.
I realize this question is old, but I wanted to post an alternate solution in case anyone else (like myself) found this thread while having the same question. I was able to work around the lack of an OR operator by echoing the variable and using findstr to validate.
for /f %%v in ('echo %var% ^| findstr /x /c:"1" /c:"2"') do (
if %errorlevel% equ 0 echo true
)
While dbenham's answer is pretty good, relying on IF DEFINED can get you in loads of trouble if the variable you're checking isn't an environment variable. Script variables don't get this special treatment.
While this might seem like some ludicrous undocumented BS, doing a simple shell query of IF with IF /? reveals that,
The DEFINED conditional works just like EXIST except it takes an
environment variable name and returns true if the environment variable
is defined.
In regards to answering this question, is there a reason to not just use a simple flag after a series of evaluations? That seems the most flexible OR check to me, both in regards to underlying logic and readability. For example:
Set Evaluated_True=false
IF %condition_1%==true (Set Evaluated_True=true)
IF %some_string%=="desired result" (Set Evaluated_True=true)
IF %set_numerical_variable% EQ %desired_numerical_value% (Set Evaluated_True=true)
IF %Evaluated_True%==true (echo This is where you do your passing logic) ELSE (echo This is where you do your failing logic)
Obviously, they can be any sort of conditional evaluation, but I'm just sharing a few examples.
If you wanted to have it all on one line, written-wise, you could just chain them together with && like:
Set Evaluated_True=false
IF %condition_1%==true (Set Evaluated_True=true) && IF %some_string%=="desired result" (Set Evaluated_True=true) && IF %set_numerical_variable% EQ %desired_numerical_value% (Set Evaluated_True=true)
IF %Evaluated_True%==true (echo This is where you do your passing logic) ELSE (echo This is where you do your failing logic)
Never got exist to work.
I use
if not exist g:xyz/what goto h:
Else xcopy c:current/files g:bu/current
There are modifiers /a etc. Not sure which ones. Laptop in shop. And computer in office. I am not there.
Never got batch files to work above Windows XP
A much faster alternative I usually use is as follows, as I can "or" an arbitrary number of conditions that can fit in variable space
#(
Echo off
Set "_Match= 1 2 3 "
)
Set /a "var=3"
Echo:%_Match%|Find " %var% ">nul || (
REM Code for a false condition goes here
) && (
REM code for a true condition goes here.
)
it's quite simple, just use below
IF %var% == 1 (
ECHO TRUE)
IF %var% == 2 (
ECHO TRUE)
Another option is to display the current environment variables and exploit the default behaviour of FINDSTR:
FINDSTR "hello there" x.y searches for "hello" or "there" in file x.y.
So
SET | FINDSTR /I /X "var=1 var=2" >NUL
IF %ERRORLEVEL% EQU 0 (
ECHO TRUE
) ELSE (
ECHO FALSE
)
Where
/I Specifies that the search is not to be case-sensitive.
/X Prints lines that match exactly.
If regular expressions are preferred, use FINDSTR /R /I "^var=1$ ^var=2$" >NUL instead.
Edit: FINDSTR /R should be used if the variable string includes a space, e.g., FINDSTR /R /I "^var=1 a$ ^var=2 b$" >NUL.
Edit: If the variable string includes spaces, a literal search string should be used. E.g., FINDSTR /I /X /C:"var=1 a" /C:"var=2 b" >NUL.
There is no OR operator but you can write (the pseudocode)
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
like
IF "%var%" == "1" SET "match=y"
IF "%var%" == "2" SET "match=y"
IF DEFINED match ECHO TRUE
Note that the double quotes prevents a syntax error from being triggered if var is undefined.
I took bogdan's solution to the next level by building an extern function that is a callable and clean abstraction of IF, so it can be used in inline blocks. Don't look further, if you build a batch library anyways.
lib.cmd
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS
SHIFT & GOTO:%1
: Common batch extension library.
:::
: Performs conditional processing in batch programs. Is callable for inline use.
: Arguments:
: %1 - /I for case-insensitive comparison on strings, can be skipped for case-sensitive comparison.
: %2 - NOT to negate the result, can be skipped.
: %3 - EXIST for file checks or first string to compare with, also supports "string1"=="string2" (full condition).
: %4 - If EXIST is specified, path to the directory or file.
: Otherwise if %3 is not a full condition, this argument has to be one of:
: - == Compares both strings to be equal using lstrcmpW or lstrcmpiW (case-insensitive).
: - EQU Converts both strings to numbers and checks if they are equal.
: - NEQ Converts both strings to numbers and checks if they are not equal.
: - LSS Converts both strings to numbers and checks if the first is lesser than the second.
: - LEQ Converts both strings to numbers and checks if the first is lesser or equal than the second.
: - GTR Converts both strings to numbers and checks if the first is greater than the second.
: - GEQ Converts both strings to numbers and checks if the first is greater or equal than the second.
: If a string cannot be parsed to a number, its numeric representation will be used.
: This argument can be skipped, so == will be used.
: %5 - If %3 is not a full condition, this argument has to be the second string to compare.
: Outputs:
: Nothing
: Returns:
: 0, if the condition is met, 1 otherwise
:::
:test-if
IF "%~1"=="/I" SET "I=/I " & SHIFT
IF "%~1"=="/i" SET "I=/I " & SHIFT
IF "%~1"=="NOT" SET "NOT=NOT " & SHIFT
IF "%~1"=="not" SET "NOT=NOT " & SHIFT
IF "%~1"=="EXIST" SET "EXIST=EXIST " & SHIFT
IF "%~1"=="exist" SET "EXIST=EXIST " & SHIFT
SET "string1=%~1%"
IF "%~3"=="" (
SET "comp=^=^="
SET "string2=%~2"
) ELSE (
SET "comp=%~2"
SET "string2=%~3"
)
IF %I%%NOT%%EXIST% "%string1%" %comp% "%string2%" (
ENDLOCAL & EXIT /B 0
)
ENDLOCAL & EXIT /B 1
Usage
( ( CALL lib test-if "%1" == "foo" ) || ( CALL lib test-if "%1" == "bar" ) ) && (
ECHO "Argument is foo or bar"
)
lib is the path to the lib.cmd, the suffix .cmd is not mandatory for cmd-files
Any IF syntax is compatible with this abstraction, so you can also do things like test-if EXIST "path" or test-if not 300 LSS 200
The == in test-if "%1" == "foo" will be stripped away by batch and I address this fact in my case, but this causes test-if "%1" "foo" to be valid as well, it's not the standard though.
Realizing this is a bit of an old question, the responses helped me come up with a solution to testing command line arguments to a batch file; so I wanted to post my solution as well in case anyone else was looking for a similar solution.
First thing that I should point out is that I was having trouble getting IF ... ELSE statements to work inside of a FOR ... DO clause. Turns out (thanks to dbenham for inadvertently pointing this out in his examples) the ELSE statement cannot be on a separate line from the closing parens.
So instead of this:
FOR ... DO (
IF ... (
)
ELSE (
)
)
Which is my preference for readability and aesthetic reasons, you have to do this:
FOR ... DO (
IF ... (
) ELSE (
)
)
Now the ELSE statement doesn't return as an unrecognized command.
Finally, here's what I was attempting to do - I wanted to be able to pass several arguments to a batch file in any order, ignoring case, and reporting/failing on undefined arguments passed in. So here's my solution...
#ECHO OFF
SET ARG1=FALSE
SET ARG2=FALSE
SET ARG3=FALSE
SET ARG4=FALSE
SET ARGS=(arg1 Arg1 ARG1 arg2 Arg2 ARG2 arg3 Arg3 ARG3)
SET ARG=
FOR %%A IN (%*) DO (
SET TRUE=
FOR %%B in %ARGS% DO (
IF [%%A] == [%%B] SET TRUE=1
)
IF DEFINED TRUE (
SET %%A=TRUE
) ELSE (
SET ARG=%%A
GOTO UNDEFINED
)
)
ECHO %ARG1%
ECHO %ARG2%
ECHO %ARG3%
ECHO %ARG4%
GOTO END
:UNDEFINED
ECHO "%ARG%" is not an acceptable argument.
GOTO END
:END
Note, this will only report on the first failed argument. So if the user passes in more than one unacceptable argument, they will only be told about the first until it's corrected, then the second, etc.

Batch File input validation - Make sure user entered an integer

I'm experimenting with a Windows batch file to perform a simple operation which requires the user to enter a non-negative integer. I'm using simple batch-file techniques to get user input:
#ECHO OFF
SET /P UserInput=Please Enter a Number:
The user can enter any text they want here, so I would like to add some routine to make sure what the user entered was a valid number. That is... they entered at least one character, and every character is a number from 0 to 9. I'd like something I can feed the UserInput into. At the end of the routine would be like an if/then that would run different statements based on whether or not it was actually a valid number.
I've experimented with loops and substrings and such, but my knowledge and understanding is still slim... so any help would be appreciated.
I could build an executable, and I know there are nicer ways to do things than batch files, but at least for this task I'm trying to keep it simple by using a batch file.
You're probably not doing this in a DOS batch file. Or at least, support for set /p is unheard of for me in DOS :-)
You could use substrings. In fact I have written a parser for a specific regular language that way once, but it's cumbersome. The easiest way would probably be to assign the contents of %userinput% to another variable, using set /a. If the result comes out as 0 you need to check whether the input itself was 0, otherwise you can conclude it was a non-number:
#echo off
setlocal enableextensions enabledelayedexpansion
set /p UserInput=Enter a number:
set /a Test=UserInput
if !Test! EQU 0 (
if !UserInput! EQU 0 (
echo Number
) else (
echo Not a number
)
) else (
echo Number
)
However, this works only for numbers in the range of Int32. If you just care for any number (possibly floating-point as well) then you need to resort to the loop-based approach of dissecting it.
NOTE: Updated to solve the space issues. However, there is still a problem lurking: Entering 123/5 yields "number", since set /a can evaluate this ...
Thanks all. I was trying to make it harder for myself looking at loops and string manipulation. I used your tips on math evaluation and comparison. Here's what I finally came up with as my concept script:
:Top
#ECHO OFF
ECHO.
ECHO ---------------------------------------
SET /P UserInput=Please Enter a Number:
ECHO.
ECHO UserInput = %UserInput%
ECHO.
SET /A Evaluated=UserInput
ECHO Math-Evaluated UserInput = %Evaluated%
if %Evaluated% EQU %UserInput% (
ECHO Integer
IF %UserInput% GTR 0 ( ECHO Positive )
IF %UserInput% LSS 0 ( ECHO Negative )
IF %UserInput% EQU 0 ( ECHO Zero )
REM - Other Comparison operators for numbers
REM - LEQ - Less Than or Equal To
REM - GEQ - Greater Than or Equal To
REM - NEQ - Not Equal To
) ELSE (
REM - Non-numbers and decimal numbers get kicked out here
ECHO Non-Integer
)
GOTO Top
This method catches all numbers and can detect whether it's positive, negative, or zero. Any decimal or string will be detected as non-integers. The only edge case I've found is a string with spaces. For example, the text "Number 1" will cause the script to crash/close when the user input is evaluated as math. But in my situation, this is fine. I don't want my script to go on with invalid input.
You can also use a quite simple trick:
echo %userinput%|findstr /r /c:"^[0-9][0-9]*$" >nul
if errorlevel 1 (echo not a number) else (echo number)
This uses findstr's regular expression matching capabilities. They aren't very impressive but useful at times.
This is the same idea as that of Johannes..
SET /A sets a numeric value. If the input is not a number, it changes it to 0.
That's what you can exploit here to do your check.
#ECHO OFF
SET /P UserInput=Please Enter a Number:
IF %UserInput% EQU 0 GOTO E_INVALIDINPUT
SET /A UserInputVal="%UserInput%"*1
IF %UserInputVal% GTR 0 ECHO UserInput "%UserInputVal%" is a number
IF %UserInputVal% EQU 0 ECHO UserInput "%UserInputVal%" is not a number
GOTO EOF
:E_INVALIDINPUT
ECHO Invalid user input
:EOF
As an alternative, you could always create a little javascript file and call it from your batchfile. With parseInt() you could force the input to be an integer, or you could roll your own function to test the input.
Writing the javascript is just as fast as the batchfile, but it's much more powerful. No IDE or compiler required; notepad will do. Runs on every windows box, just like your batchfiles. So why not make use of it?
You can even mix batchfiles and javascript. Example:
contents of sleep.js:
var SleepSecs=WScript.Arguments.Item(0);
WScript.Sleep(SleepSecs*1000)
contents of sleep.cmd:
cscript /nologo sleep.js %1
You can now call this from a batchfile to make your script sleep for 10 seconds. Something like that is difficult to do with just a plain batchfile.
sleep 10
As pointed out by ghostdog74, the answers posted by Joey Mar 26 '09 (score 10) and Wouter van Nifterick Mar 26 '09 (score 5) don't work.
The answer posted by Joey Mar 25 '10 (score 2) does work, except that redirection symbols and '&' cause syntax errors.
I think the best and simplest solution is the one posted by Sager Oct 8 '14 (score 0). Unfortunately, it has a typo: ‘"%a"’ should be ‘"%a%"’.
Here's a batch file based on Sager's answer. Redirection symbols and '&' in the input don't cause problems. The only problems I could find were caused by strings containing double quotes.
#echo off & setlocal enableextensions & echo.
set /p input=Enter a string:
SET "x=" & for /f "delims=0123456789" %%i in ("%input%") do set x=%%i
if defined x (echo Non-numeral: "%x:~0,1%") else (echo No non-numerals)
In addition to the remark about the error that occures when spaces are part of the users input. You can use errorlevel errorlevel=9165. It can be used for the spaces in a string or for the error handling of 'no' input.
Kind Regards,
Egbert
You might also like this one - it's short and easy. This one use the multiplication trick to set TestVal. Comparing TestVal against UserInput allows all numeric values to get through including zeroes, only non-numerics will trigger the else statement. You could aslo set ErrorLevel or other variables to indicate a failed entry
#ECHO OFF
SET TestVal=0
SET /P UserInput=Please Enter a Number:
SET /A TestVal="%UserInput%"*1
If %TestVal%==%UserInput% (
ECHO You entered the number %TestVal%
) else ECHO UserInput "%UserInput%" is not a number
GOTO EOF
:EOF
I know this is years old, but just to share my solution.
set /p inp=Int Only :
:: Check for multiple zeros eg : 00000 ::
set ch2=%inp%-0
if %inp% EQU 0 goto :pass
if [%inp%]==[] echo Missing value && goto :eof
if %inp:~0,1%==- echo No negative integers! && goto :eof
set /a chk=%inp%-10>nul
if %chk%==-10 echo Integers only! && goto :eof
:pass
echo You shall pass
:eof
Tested and working on Windows 8.
you can reinvent the wheel and grow a few white hairs doing string validation in batch, or you can use vbscript
strInput = WScript.Arguments.Item(0)
If IsNumeric(strInput) Then
WScript.Echo "1"
Else
WScript.Echo "0"
End If
save it as checkdigit.vbs and in your batch
#echo off
for /F %%A in ('cscript //nologo checkdigit.vbs 100') do (
echo %%A
rem use if to check whether its 1 or 0 and carry on from here
)
You can validate any variable if its number:
SET "var="&for /f "delims=0123456789" %i in ("%a") do set var=%i
if defined var (echo."NIC">nul) else (echo."number")
If you want some sort of a loop and default set up for that particular question, then here's my method for doing this.
Notes on the code within.
#echo off
setlocal EnableDelayedExpansion
set "ans1_Def=2"
:Q1
set /p "ans1=Opt 1 of 1 [Value 1-5 / Default !ans1_Def!]: "
:: If not defined section. This will use the default once the ENTER key has been
:: pressed and then go to :Q2.
if not defined ans1 (
echo/ & echo ENTER hit and the default used. Default is still: !ans1_Def! & echo/
set "ans1=!ans1_Def!" && goto :Q2 )
:: This section will check the validity of the answer. The "^[1-5]$" will work
:: for only numbers between one and five in this example but this can be changed
:: to pretty much suit the majority of cases. This section will also undefine
:: the ans1 variable again so that hitting the ENTER key at the question
:: will work.
echo %ans1%|findstr /r /c:"^[1-5]$" >nul
if errorlevel 1 (
echo/ & echo At errorlevel 1. Wrong format used. Default is still: !ans1_Def! & echo/
set "ans1=" && goto Q1
) else ( echo Correct format has been used. %ans1% is the one. && goto :Q2 )
:Q2
echo/
echo -----------------------------
echo/
echo Now at the next question
echo !ans1!
echo/
pause
exit
Try this:
set /p numeric=enter a number
(
(if errorlevel %numeric% break ) 2>nul
)&&(
echo %numeric% is numeric
)||(
echo %numeric% is NOT numeric
)
Just try this
#echo off
SET constNum=100
:LOOP
Set /p input=Please input a number less than %constNum% :
if "%input%" == "" echo Blank is not allowed & goto LOOP
SET "notNumChar="
for /f "delims=0123456789" %%i in ("%input%") do set notNumChar=%%i
if defined notNumChar (
echo %input% is a string
goto LOOP
) else (
REM Remove leading 0 if it has. eg: 08→8
FOR /F "tokens=* delims=0" %%A IN ("%input%") DO SET inputNum=%%A
)
REM Compare
if defined inputNum (
echo %inputNum%
if %inputNum% equ %constNum% & goto LOOP
if %inputNum% gtr %constNum% & goto LOOP
if %inputNum% lss %constNum% & goto CONTINUE
)
:CONTINUE
:: Your code here
:ASK
SET /P number= Choose a number [1 or 2]:
IF %number% EQU 1 GOTO ONE
IF %number% NEQ 1 (
IF %number% EQU 2 GOTO TWO
IF %number% NEQ 2 (
CLS
ECHO You need to choose a NUMBER: 1 OR 2.
ECHO.
GOTO ASK
)
)
It works fine to me. If he chooses numbers less or greater, strings, floating number etc, he wil receive a message ("You need to choose a NUMBER: 1 OR 2.") and the INPUT will be asked again.
#echo off
setlocal enableextensions enabledelayedexpansion
set /p UserInput=Enter a number:
set /a Test=UserInput
if !Test! EQU 0 (
if !UserInput! EQU 0 (
echo Number
) else (
echo Not a number
)
) else (
echo Number
)
yeaph everthing is great
but you forget about one little thing
0 also is a digit
;(
This is more of a user friendly way.
if %userinput%==0 (
cls
goto (put place here)
)
if %userinput%==1 (
cls
goto (put place here)
)
if %userinput%==2 (
cls
goto (put place here)
)
if %userinput%==3 (
cls
goto (put place here)
)
if %userinput%==4 (
cls
goto (put place here)
)
if %userinput%==5 (
cls
goto (put place here)
)if %userinput%==6 (
cls
goto (put place here)
)if %userinput%==7 (
cls
goto (put place here)
)
if %userinput%==8 (
cls
goto (put place here)
)
if %userinput%==9 (
cls
goto (put place here)
)
This can be used for any type of user input.
for me this is working for all non-zero values ..should i be cautious of some rare cases?
set /a var = %1
if %var% neq 0 echo "it is number"
pause

Resources