Win batch: setting variable within nested loop not working - windows

I have a long script that I have condensed to the following lines of code to illustrate the issue I am having. I have tried some suggestion by StackOverflow users to no avail, so hopefully your feedback will help me and future users. NOTE: this code works, except for setting the pdfREP nested variable.
SETLOCAL enabledelayedexpansion
set pdfREP=false
for /f "tokens=1" %%a in ('dir /o /b \\path2document\*.rp?') do (
findstr "," \\path2log\%%a > 1.log
if not errorlevel 0 (
:: do something
)
if errorlevel 0 (
findstr /B /I "p" \\path2document\%%a > 1.log
if errorlevel == 0 (
set pdfREP=true
echo RSP File: %%a >> 2.log
)
)
)
Basically the issue is that in \path2document I have multiple files, and within each I look for a comma. If no comma is found then I want to know if there is a particular letter inside the file's text. If the text is found, the I am setting a previously defined variable to TRUE, instead of FALSE. However, the "if errorlevel == 0" can be true if different syntax (%errorlevel%==0,%errorlevel% EQU 0), and it will NOT set the variable pdfREP to TRUE. If the issue is that the variable is not set until after the loop iteration, then how can I use this variable in the rest of my code? I would like to use this variable later on, so setting it is most important. Thanks for any feedback.

Windows batch has an "interesting" way of handling nested variables. This article might help.
Personally, when my batch files get this complex, I switch to a different language. My first choice is generally Python, but if you'd like to stay inside the Microsoft ecosystem, then vbscript or PowerShell would work.

You are misusing the IF command and the errorlevel value.
IF command description indicate that you may directly use in the condition the ERRORLEVEL word followed by a number indicating a given errorlevel. This way, the following two IF commands are right:
if not errorlevel 0 (
:: do something
)
if errorlevel 0 (
However, the following command is bad written:
if errorlevel == 0 (
In this case, you must use !errorlevel! to indicate to take the current errorlevel value after executing the last line:
if !errorlevel! == 0 (
Independently of the above said, this is the way that I would do that:
if not errorlevel 0 (
echo The errorlevel is less than zero
) else if errorlevel 0 (
echo The errorlevel is greater than zero
) else (
echo The errorlevel is zero
)

Related

If statement ( was unexpected at this time

I have a bundled if statement that checks whether java is installed and whether or not i'm at college or at home it is part of a larger piece of code but doesn't work on its own either any help would be much appreciated
Triple Checked i have the right number of brackets and equals signs and making sure no obvious mistakes were made so i'm confused as to what has gone wrong
if exist 'C:\Temporary Workspace' (set installtype=College)
where java >nul 2>nul
pause
if %errorlevel%==1 (
if %installtype%==College (
goto :college
) else (
set /P javaboolean=Java was not found is java installed? (Y/N)
if %javaboolean%==Y (
echo Please enter the path to java.exe
set /P javalocation=e.g. C:\Program Files\Common Files\Oracle\Java\javapath\java.exe
)
)
)
pause
exit
:college
echo success for college detection
pause
The error message and run time:
D:\Batch Testing>echo Checking Location...
Checking Location...
D:\Batch Testing>if exist 'C:\Temporary Workspace' (set installtype=College)
D:\Batch Testing>where java 1>nul 2>nul
D:\Batch Testing>pause
Press any key to continue . . .
( was unexpected at this time.
I expect the output to be 'success for college detection' as the directory does exist
you need to change your ifs a little bit
the first could be changed to
if exist "C:\Temporary Workspace" set installtype=College
note that the single apostrophe is not a valid quotation mark in windows cmd. And you can remove your parentheses because they are not needed.
the second could be written as
if errorlevel 1 (
because errorlevel is not %errorlevel% (for a detailed explanation, read https://devblogs.microsoft.com/oldnewthing/20080926-00/?p=20743 )
and the last could be
if .%installtype%==.College (
because when installtype is blank the if instruction makes no sense and produces syntax error; inserting the two . avoids it.
... and, as a bonus, you might want to consider to use if /i to ignore case when comparing, so College, college, COLLEGE or even cOllegE would match.
Well, there are several issues in your code. The line
if exist 'C:\Temporary Workspace' (set installtype=College)
will never set the variable installtype, because this checks whether 'C:\Temporary exists, which does not, since the apostrope ' is just an ordinary character for the Windows Command Prompt cmd. To make it work you must use quotation marks ":
if exist "C:\Temporary Workspace\" (set "installtype=College")
The trailing \ lets the condition check for existence of a directory called Temporary Workspace but not for a file with that name.
Note that I used the quoted set syntax here, which is generally recommended as it protects special characters.
The condition
if %installtype%==College (
will return a syntax error, because the variable installtype has not been set before (due to the wrong syntax you used). To avoid trouble with empty variables, quote both comparison expressions:
if "%installtype%"=="College" (
This is also described in this thread.
The command
set /P javaboolean=Java was not found is java installed? (Y/N)
will fail when in a parenthesised block of code, because the ) will unintentionally be recognised by cmd and close the block at an undesired position. Again use quotation to solve that:
set /P javaboolean="Java was not found is java installed? (Y/N)"
or:
set /P "javaboolean=Java was not found is java installed? (Y/N)"
However, the condition
if %javaboolean%==Y (
will still fail, because the queried variable javaboolean is set in the same block of code, so the returned value is the one present when the entire block is parsed, which is an empty string most probably. To overcome this, you must apply delayed variable expansion.
Put this somewhere before to enable delayed expansion:
setlocal EnableDelayedExpansion
Then replace the %-signs by exclamation marks ! (as you can see I again applied quotation here):
if "!javaboolean!"=="Y" (
Then put this somewhere after to end the environment localisation introduced by setlocal:
endlocal
Regard that all environment changes (variables, current directories) after setlocal are lost after endlocal and the state before setlocal becomes restored.
A similar problem is also described in this question and solved by this answer.
Instead of set /P just for a Yes/No decision consider to use the choice command, which does not allow to enter anything else:
rem /* The default choices are `Y` and `N`;
rem `choice` sets `ErrorLevel` to `1` when pressing `Y` and to `2` when pressing `N`: */
choice /M "Java was not found is java installed"
rem // The following means: if ErrorLevel >= 1 and if ErrorLevel < 2:
if ErrorLevel 1 if not ErrorLevel 2 (echo Yes has been chosen.)
Instead of
exit
you should use
exit /B
to only terminate the batch script but keep the hosting cmd instance alive. This is particularly helpful when you execute the batch file from a Command Prompt window rather than by double-clicking its icon in Explorer as you can view the console output then.
Instead of the string comparison
if %errorlevel%==1 (
you could also do true numeric comparison like
if %errorlevel% equ 1 (
or, if you are fine with the condition to be true if ErrorLevel is also greater than one,
if ErrorLevel 1 (
Alternatively, you could even use condition execution operators, which query the exit code:
where java > nul 2>&1 && (
rem // The code here is executed when `where` succeeds.
) || (
rem // The code here is executed when `where` fails.
)
So finally, here is the fixed script:
Code based on yours:
if exist "C:\Temporary Workspace\" (set "installtype=College")
where java > nul 2>&1
pause
if ErrorLevel 1 (
if "%installtype%"=="College" (
goto :college
) else (
set /P javaboolean="Java was not found is java installed? (Y/N) "
setlocal EnableDelayedExpansion
if "!javaboolean!"=="Y" (
endlocal
echo Please enter the path to java.exe
set /P javalocation="e.g. C:\Program Files\Common Files\Oracle\Java\javapath\java.exe "
) else endlocal
)
)
pause
exit /B
:college
echo success for college detection
pause
Variant using conditional execution (||) and the choice command:
if exist "C:\Temporary Workspace\" (set "installtype=College")
where java > nul 2>&1 || (
if "%installtype%"=="College" (
goto :college
) else (
choice /M "Java was not found is java installed"
if ErrorLevel 1 if not ErrorLevel 2 (
echo Please enter the path to java.exe
set /P javalocation="e.g. C:\Program Files\Common Files\Oracle\Java\javapath\java.exe "
)
)
)
pause
exit /B
:college
echo success for college detection
pause

Filtering for multiple results in batch using IF

I was writing a batch file replicating CMD but more customized. What I am trying to do is scan %input% for multiple different results leading to different actions. To help you envision what I mean, I tried doing this:
set /p input="%cd%>"
if "%input%" == "cls" (
GOTO reset
) else (
if "%input%" == freespace (
GOTO freespace
) else (
title %input%
%input%
GOTO A
Which just crashes the CMD window running the batch file. Is there anyway I can sort for these two responses (or possibly more) using IF statements? I realize this is SIMILAR to other questions called "using multiple if statements in batch" but they are just not the same format I am trying to put the IF statements in.
You appear to be running your batch by clicking on it, which means that you will not see error messages. It's better to run batch from the command-prompt so that the messages will be visible and persistent.
There are at least two problems with the code you have presented.
The first is that you have not closed the parentheses for the else clauses so there are two pending close-parentheses at the end of the batch.
format:
if x=y (dothis
) else (
if p==q (dosomethingelse
) else (
dosomethingelseentirely
)
)
The second problem is that if is very literal with a string-match. It includes the quotes in the strings-to-be-matched, so
if "%input%" == freespace (
can never be true as input is quoted but freespace is not (unlike cls in your first if)
BTW - if /i ... will perform a case-insensitive comparison.
You don't need multiple if commands to filter multiple results in this particular problem. This is the way I would do it:
#echo off
setlocal
:A
echo/
set "input="
set /p "input=%cd%>"
call :%input% 2> NUL
if errorlevel 1 echo "%input%" is not recognized as a command...
goto A
:cls
echo Reset command, parameters: "%*"
exit /B 0
:freespace
echo FreeSpace command, parameters: "%*"
exit /B 0
In this method a call :%input% command is directly executed, so if the label exists, then the corresponding code run; otherwise, the call :nolabel command returns an ERRORLEVEL equal 2.
Each one of the subroutines ends with exit /B 0 command, so in these cases the ERRORLEVEL is zero.
If you have any doubt about a particular command, I encourage you the review its command /? help screen or search the web looking for a more extensive description...

find string in text file and then renaming my log file

im working on a basic .bat file.
It checks if various files exists and if they dont, it will write "ERROR" in the log file.
I then test this log file for the string "ERROR" and if it does I want to rename my log file, but i seem to be getting an error on my if statement. Heres my code..
set "filename=C:\Temp\%COMPUTERNAME%.txt"
echo Creating .txt file...
echo Getting the Computer name...
echo %COMPUTERNAME% >> filename
echo ArcGIS Desktop 10 File checker
pause
echo Looking for files.....
call:checkFileExists C:\support\msi_local\Oracle10g\marker.txt
pause
FIND "ERROR" filename
echo error level is %ERRORLEVEL%
pause
if %ERRORLEVEL% 1 (
set "newfileName=C:\Temp\%COMPUTERNAME%%_ERROR.txt"
rename fileName newfileName
)
pause
:checkFileExists
if exist %~1 (
echo Success %~1 does exist >> C:\Temp\%COMPUTERNAME%.txt
) else (
echo ERROR "C:\support\msi_local\Oracle10g\marker.txt"%~1 does not exist >> C:\Temp\%COMPUTERNAME%.txt
)
I get a error -
The syntax of teh command is incorrect.
C:\Windows>if ERRORLEVEL 1(
Where am i going wrong?
Thanks
try this:
#echo off &SETLOCAL
set "filename=C:\Temp\%COMPUTERNAME%.txt"
echo Creating .txt file...
echo Getting the Computer name...
>>"%filename%" echo %COMPUTERNAME%
echo ArcGIS Desktop 10 File checker
pause
echo Looking for files.....
call:checkFileExists "C:\support\msi_local\Oracle10g\marker.txt"
pause
FIND "ERROR" "%filename%"
echo error level is %ERRORLEVEL%
pause
if %ERRORLEVEL% equ 1 (
set "newfileName=C:\Temp\%COMPUTERNAME%_ERROR.txt"
move "%fileName%" "%newfileName%"
)
pause
:checkFileExists
if exist "%~1" (
echo Success %~1 does exist >> C:\Temp\%COMPUTERNAME%.txt
) else (
echo ERROR "C:\support\msi_local\Oracle10g\marker.txt"%~1 does not exist >> C:\Temp\%COMPUTERNAME%.txt
)
if %ERRORLEVEL% 1 (
should be
if ERRORLEVEL 1 (
And, since you're retyping the error rather than copy-pasting, note that there must be a space between the 1 and the (
The error appears that way because batch replaces any %var% with its value at that time (when it is 'parsed') and then executes the line, so batch substitutes whatever has been reported as %errorlevel% from your debug statement :) (eg 1) and then tries valiantly to work out what if 1 1 ( means.
(btw, it would be a good idea to replace if exist %~1 ( with if exist "%~1" (
This may seem redundant, removing and then replacing the quotes, BUT if you later decide to change the statement to if exist %file% ( it's only later you'll find out you'll get a crash when %file% contains a space. Best to be ever-mindful of the spaces-in-filenames problem; if you make quoting a habit, you'll be caught out less often.
)
The two syntaxes are subtly different:
Old style syntax (MS-DOS era) - check if the error level was 1 or more:
IF ERRORLEVEL 1
New style syntax - check if the %errorlevel% variable was not equal to 0:
IF %errorlevel% NEQ 0
Note that the new style syntax uses % to indicate a variable, while with the old style syntax, ERRORLEVEL is a special keyword.
The new style syntax should be preferred, because it will handle programs that return -1 on error. If the program can return negative error codes on success, you could handle errors with IF %errorlevel GEQ 1. Either way, using the %errorlevel% variable allows a lot more flexibility.
For clarity I've used upper case for keywords and lower case for variables, but it should be case sensitive either way.

Foolproof way to check for nonzero (error) return code in windows batch file

Intro
There's a lot of advice out there for dealing with return codes in batch files (using the ERROLEVEL mechanism), e.g.
Get error code from within a batch file
ERRORLEVEL inside IF
Some of the advice is to do if errorlevel 1 goto somethingbad, while others recommend using the
%ERRORLEVEL% variable and using ==, EQU, LSS, etc. There seem to be issues within IF statements and such, so then delayedexpansion is encouraged, but it seems to come with quirks of its own.
Question
What is a foolproof (i.e. robust, so it will work on nearly any system with nearly any return code) way to know if a bad (nonzero) code has been returned?
My attempt
For basic usage, the following seems to work ok to catch any nonzero return code:
if not errorlevel 0 (
echo error level was nonzero
)
Sorry, your attempt is not even close. if not errorlevel 0 is only true if errorlevel is negative.
If you know that errorlevel will never be negative, then
if errorlevel 1 (echo error level is greater than 0)
If you must allow for negative errorlevel, and are not within a parenthesized block of code, then
set "errorlevel=1"
set "errorlevel="
if %errorlevel% neq 0 (echo error level is non-zero)
Note - I edited my answer to explicitly clear any user defined errorlevel value after reading Joey's comment to the linked answer in the question. A user defined errorlevel can mask the dynamic value that we are trying to access. But this only works if your script has a .bat extension. Scripts with .cmd extension will set your ERRORLEVEL to 0 if you set or clear a variable! To make matters worse, XP will set ERRORLEVEL to 1 if you attempt to undefine a variable that does not exist. That is why I first explicitly define an ERRORLEVEL variable before I attempt to clear it!
If you are within a parenthesized block of code then you must use delayed expansion to get the current value
setlocal enableDelayedExpansion
(
SomeCommandThatMightGenerateAnError
set "errorlevel=1"
set "errorlevel="
if !errorlevel! neq 0 (echo error level is non-zero)
)
But sometimes you don't want delayed expansion enabled. All is not lost if you want to check the error level immediately after executing a command.
(
SomeCommandThatMightGenerateAnError && (echo Success, no error) || (echo There was an error)
)
If you absolutely must check the dynamic ERRORLEVEL value without using delayed expansion within a parenthesized block, then the following works. But it has the error handling code in two places.
(
SomeCommandThatMightGenerateAnError
if errorlevel 1 (echo errorlevel is non-zero) else if not errorlevel 0 (echo errorlevel is non-zero)
)
Here, at long last, is the "ultimate" test for non-zero errrolevel that should work under any circumstances :-)
(
SomeCommandThatMightGenerateAnError
set foundErr=1
if errorlevel 0 if not errorlevel 1 set "foundErr="
if defined foundErr echo errorlevel is non-zero
)
It can even be converted into a macro for ease of use:
set "ifErr=set foundErr=1&(if errorlevel 0 if not errorlevel 1 set foundErr=)&if defined foundErr"
(
SomeCommandThatMightGenerateAnError
%ifErr% echo errorlevel is non-zero
)
The macro supports parentheses and ELSE just fine:
%ifErr% (
echo errorlevel is non-zero
) else (
echo errorlevel is zero
)
One last issue:
Redirection of input and/or output can fail for any number of reasons. But redirection errors do not set the errorlevel unless the || operator is used. See File redirection in Windows and %errorlevel% for more information. So one can argue that there does not exist a fool-proof way to check for errors via errorlevel. The most reliable method (but still not infallible) is the || operator.

Batch file set /p expressions

I'm attempting to make the errorlevel environment variable increase by one every time a certain section of my code is ran. I read in set /? that you can use /p to list an expression that needs to be calculated after the equals sign, however it doesn't seem to change the errorlevel at all.
This is what I have.
if not %cd2%==%cd1% (goto :installauto) else set /p errorlevel=(%errorlevel%+1)
Thanks for the help, and sorry if this is a noob question. >.<
EDIT: Wow, I'm an idiot. The /a tag is used for expressions. Sorry lol.
It's a bad idea to change/create an errorlevel variable, as this is not the errorlevel for other programs.
Then you can not access the "real" errorlevel anymore.
You could better do this with an other variable and exit the bacth file with exit /B
set myErr=0
if not %cd2%==%cd1% (
goto :installauto
) else (
set /a myErr=myErr+1
)
exit /b %myErr%

Resources