Windows Batch For Loop: having trouble with the following code - for-loop

set checker=0
for %%a in (%namelist%) do (
:startLoop
findstr "completed" %%a_Logs.txt
IF ERRORLEVEL 1 (
IF %checker%==120 (
set checker=0
goto endLoop
)
set /a checker=%checker%+1
#ping 127.0.0.1 -n 1 -w 1000 > nul
findstr "ERROR" %%a_Logs.txt
IF ERRORLEVEL 1 (
echo Waiting 1 second before rechecking (Max 2 mins)
echo time elapsed %checker% seconds
echo.
goto startLoop
)
findstr "ERROR" %%a_Logs.txt
IF NOT ERRORLEVEL 1 (
echo ERROR: %%a Error found
goto endLoop
)
)
findstr "completed" %%a_Logs.txt
IF NOT ERRORLEVEL 1 (
echo %%a completed
)
:endLoop
)
The above piece of code is to do the following:
Parse the variable namelist(where the contents are separated by spaces)
Check if "completed" is present in the %%a_Logs.txt file
If it is present, then iteration over, If it is not, then check for the string "ERROR" in same file
If ERROR is present, then output ERROR MSG and end iteration
If ERROR is not found, keep rechecking for the next 120 seconds before ending iteration
I keep getting the following output
FINDSTR: Cannot open %a_Logs.txt

You are attempting to GOTO a label within a FOR loop - that simply doesn't work. The moment a FOR loop executes GOTO, the loop is terminated, and the FOR context is lost. So your %%a FOR variable is no longer defined. A similar issue happens with IF statements, as described at (Windows batch) Goto within if block behaves very strangely.
You also have a problem when you attempt to expand %checker% within the same parenthesized code block that sets the value. That expansion occurs at parse time, and the entire block is parsed at once. So the value you see will always be the value that existed before the block was entered. The solution is to enable delayed expansion and use !checker! instead of %checker%.
Personally, I would probably make significant changes to your code. But I believe the following minimal changes can make your code work, assuming there are no other bugs:
enable delayed expansion
Move your DO loop code to a routine outside of the loop, and then have the loop CALL that routine with %%a as a parameter. CALL does not break the loop.
Substitute %1 for %%a in the routine
Substitute exit /b for goto endLoop. Also put exit /b at end of the routine
Make sure the code does not fall into the routine when the FOR loop finishes. I used a GOTO after the FOR loop
Substitute !checker! for %checker%
EDIT -The ) in the ECHO statement must be escaped
Here is the modified code (untested)
setlocal enableDelayedExpansion
set checker=0
for %%a in (%namelist%) do call :startLoop %%a
goto continue
:startLoop
findstr "completed" %1_Logs.txt
IF ERRORLEVEL 1 (
IF !checker!==120 (
set checker=0
exit /b
)
set /a checker=checker+1
#ping 127.0.0.1 -n 1 -w 1000 > nul
findstr "ERROR" %1_Logs.txt
IF ERRORLEVEL 1 (
echo Waiting 1 second before rechecking (Max 2 mins^)
echo time elapsed !checker! seconds
echo.
goto startLoop
)
findstr "ERROR" %1_Logs.txt
IF NOT ERRORLEVEL 1 (
echo ERROR: %1 Error found
exit /b
)
)
findstr "completed" %1_Logs.txt
IF NOT ERRORLEVEL 1 (
echo %1 completed
)
exit /b
:continue

I think the labels inside your for loop are messing it up. I just tried it moving the contents of the loop into a separate "subroutine" and that gets rid of the error you mention.
Try this:
set checker=0
for %%a in (foo bar baz) do (
call :loop %%a
)
goto :eof
:loop
set basename=%1
:startLoop
findstr "completed" %basename%_Logs.txt
IF ERRORLEVEL 1 (
IF %checker%==120 (
set checker=0
goto endLoop
)
set /a checker=%checker%+1
#ping 127.0.0.1 -n 1 -w 1000 > nul
findstr "ERROR" %basename%_Logs.txt
IF ERRORLEVEL 1 (
echo Waiting 1 second before rechecking (Max 2 mins)
echo time elapsed %checker% seconds
echo.
goto startLoop
)
findstr "ERROR" %basename%_Logs.txt
IF NOT ERRORLEVEL 1 (
echo ERROR: %basename% Error found
goto endLoop
)
)
findstr "completed" %basename%_Logs.txt
IF NOT ERRORLEVEL 1 (
echo %basename% completed
)
:endLoop
goto :eof

Related

Return a value from a called batch file label

I have two labels in my batch file. The initial label MAIN shall stay in control, so it Calls the second label, which ends with exit /b.
My script's Main label Calls the other, passing it arguments, which will be used to search strings wothin a text file.
When returning to the Calling label, it slways receives an empty return string.
I think this has something to do with the variable expansion in a loop. Who knows?
Here is the Script:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:MAIN
call :getReturnValue "1234 0815 4321 12815" "readBackVal"
if !errorlevel! equ 0 (
echo readback=!readBackVal!
echo readback=%readBackVal%
)
pause
exit /b 0
REM Function, which checks if the give return value is in a specific textfile (line for line check)
:getReturnValue
set "myExpectedValueList=%~1"
set "retval=%~2"
set "file=textexample.txt"
for %%i in (%myExpectedValueList%) do (
for /f "tokens=*" %%a in (%file%) do (
echo %%a|findstr /r "^.*%%i$"
)
if !errorlevel! equ 0 (
(endlocal
set /a "%retval%=%%i")
)
exit /b 0
)
)
exit /b 1
Here is the sample textfile textexample.txt:
Setup returns with errorcode=0815
Here is the answer i looked for:
Hi, first i want to inform that i made some changes due to the Answer of
#OJBakker. This changes are listed at the bottom of the script.
The problem was to return a value from a called function/label to the calling function/label. The stich here is, that the magic
is done in the (endlocal...) section of the called function/label -> means the return of the variable.
Before the endlocal command is executed, the compiler replaces the variables in this section by their values and afterwards executes the command´s from left to right. Means following:
First, the compiler sees following:
(endlocal
if "%retval%" neq "" (call set /a %retval%=%%i)
)
Second, the compiler replaces the variables by their values:
(endlocal
if "readBackVal" neq "" (set /a "readBackVal"=1815)
)
Third: This command is executed
(endlocal
if "readBackVal" neq "" (set /a "readBackVal"=1815)
)
Now here is my complete script (i also fixed some other problems with it which i commented at the bottom of the script
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:MAIN
setlocal
call :getReturnValue "1234 1815 4321 12815" "readBackVal"
if "!errorlevel!" equ "0" (
echo readback=!readBackVal!
)
pause
exit /b 0
REM Function, which checks if the give return value is in a specific textfile (line for line check)
:getReturnValue
setlocal
set "myExpectedValueList=%~1"
set "retval=%~2"
set "file=textexample.txt"
for %%i in (%myExpectedValueList%) do (
for /f "tokens=*" %%a in (%file%) do (
echo %%a|findstr /r "^.*%%i$" >NUL
)
if "!errorlevel!" equ "0" (
(endlocal
if "%retval%" neq "" (set /a %retval%=%%i)
)
exit /b 0
)
)
exit /b 1
REM Changes to initial posting:
REM Added "setlocal" keyword to the function "getReturnValue"
REM Corrected an invalid paranthesis in the (endlocal...) section
REM Changed the file "textexample.txt" -> 0815 to 1815 to remove leading zero (findstr. Problem),
REM Added check, if parameter "retval" has been passed to the called function e.g. is not empty
REM FINAL -> applied double variable expansion (call set /a ...) to return the value proper
REM to the :MAIN function.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:MAIN
call :getReturnValue "1234 0815 4321 12815" "readBackVal"
if %errorlevel% equ 0 (echo readback=%readBackVal%)
pause
endlocal
exit /b 0
REM Function, which checks if the give return value is in a specific textfile (line for line check)
:getReturnValue
set "myExpectedValueList=%~1"
set "retval=%~2"
set "file=textexample.txt"
for %%i in (%myExpectedValueList%) do (
for /f "tokens=*" %%a in (%file%) do (
echo %%a| >con 2>&1 findstr /r "^.*%%i$"
if !errorlevel! equ 0 (
set /a "%retval%=%%i"
exit /b 0
)
)
)
exit /b 1
rem changes:
rem endlocal moved to main.
rem check for errorlevel moved to within the commandblock of the inner for-loop.
rem 'exit /b 0' moved to within the if. This exit line stopped the for after the first item.
rem redirection added to findstr command. Now the output shows the remaining problem.
rem Invalid number. Numeric constants are either decimal (17), hexadecimal (0x11), or octal (021).
rem Findstr really does not like the value 0815, especially the starting zero.
rem I am not sure how to change the regexp so findstr won't barf at the leading zero.
rem Maybe someone else can solve this remaining problem.

Batch script error level is not getting reset [duplicate]

This question already has answers here:
ERRORLEVEL inside IF
(2 answers)
ERRORLEVEL vs %ERRORLEVEL% vs exclamation mark ERRORLEVEL exclamation mark
(1 answer)
Closed 3 years ago.
I am writing a small BAT file where it will search for "FAIL" Keyword followed by PASS - if none is found then take it as an error:
echo
set "topLevel=%cd%"
If [%1]==[] exit /B 1
If [%2]==[] exit /B 1
If [%3]==[] exit /B 1
If [%4]==[] exit /B 1
findstr /? >NUL 2>&1 || exit /B 1
set "arg1=%1"
set "arg2=%2"
set "arg3=%3"
set "arg4=%4"
set /a errno=0
if not exist %arg3% exit /B 1
if not exist %arg2%\%arg1% exit /B 1
set "logfile=%arg1:.=_%"
copy /y/v %arg2%\%arg1% %arg3%\%arg4%.%logfile%.res || exit /B 1
findstr /I /C:"FAIL" /I /C:"UNKNOWN" %arg3%\%arg4%.%logfile%.res
if %errorlevel% EQU 0 (
set /a errno=2
) ELSE (
REM MAKE SURE THAT THE SCRIPT DID NOT CRASH HENCE NEITHER PASS OR FAIL WILL BE LISTED
findstr /I /C:"PASS" %arg3%\%arg4%.%logfile%.res
if %errorlevel% NEQ 0 (
set /a errno=2
)
)
cd %topLevel%
exit /B %errno%
When I run with sample data I get below output:
..............................................
C:\agent\_work\30\s1>copy /y/v C:\output\test.log C:\agent\_work\30\s1\tttt.test_log.res || exit /B 1
1 file(s) copied.
C:\agent\_work\30\s1>findstr /I /C:"FAIL" /I /C:"UNKNOWN" C:\agent\_work\30\s1\tttt.SystemWalk_log.res
C:\agent\_work\30\s1>if 1 EQU 0 (set /a errno=2 ) ELSE (
REM MAKE SURE THAT THE SCRIPT DID NOT CRASH HENCE NEITHER PASS OR FAIL WILL BE LISTED
findstr /I /C:"PASS" C:\agent\_work\30\s1\tttt.test_log.res
if 1 NEQ 0 (set /a errno=2 )
)
PASSED
PASSED
PASSED
PASSED
PASSED
C:\agent\_work\30\s1>cd C:\agent\_work\30\s1
C:\agent\_work\30\s1>exit /B 2
C:\agent\_work\30\s1>echo %ERRORLEVEL%
2
Actually cause it has found "PASS" string and no "FAIL" ones - so the error level should be 0 - how can I fix the issue?
if %errorlevel% NEQ 0 (
should be
if errorlevel 1 (
Standard delayedexpansion issue - you need to invoke delayedexpansion [hundreds of SO articles about that - use the search feature] in order to display or use the run-time value of any variable that's changed within a parenthesised series of instructions (aka "code block").
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
IF ERRORLEVEL n is TRUE if errorlevel is n or greater than n. IF ERRORLEVEL 0 is therefore always true. IF NOT ERRORLEVEL 1 is a test for errorlevel=0. So is IF %ERRORLEVEL%==0, except that the former can be used within a block but the latter cannot.

How can I "break" out of a For loop?

Below, when I execute the goto command, it just hangs and I have to Control-C. I tried EXIT /b too. I'm trying to avoid goto's as much as possible. Is there a way to do what I want?
:SUB_bigRandLooper
set /a lcv=0
FOR /L %%s IN ( 0 , 0 , 1 ) DO (
set big-rand=!random:~-4!
echo big-rand is !big-rand!
set /a lcv=%lcv+1
if !big-rand! GTR 9900 goto bigRandLooperWrapup
)
:bigRandLooperWrapup
echo biggest-rand is %big-rand%
echo lcv is %lcv%
EXIT /B
.
The short answer is: No, you can't.
Since you are using for /L and establish an infinite loop and the fact that the loop is preprocessed and cached before it is executed, it cannot be interrupted by goto; goto breaks the loop context, or more correctly spoken, the (/) block context, so no more commands in the block are executed, but the loop itself is still running.
You can prove this by the following code:
for /L %%I in (1,1,100000) do (
echo %%I
if %%I equ 10 goto :SKIP
)
:SKIP
echo skipped
You will see that echo %%I is only executed for %%I from 1 to 10, but execution does not immediately continue at echo skipped but there is a notable delay, because the loop finishes iterating in the background, although no more commands are executed.
There is a work-around though: you could establish an endless loop with goto, like this:
:SUB_bigRandLooper
set /A lcv=0
:bigRangLooperLoop
set big-rand=!random:~-4!
echo big-rand is !big-rand!
set /A lcv+=1
if !big-rand! gtr 9900 goto :bigRandLooperWrapup
goto :bigRangLooperLoop
:bigRandLooperWrapup
echo biggest-rand is %big-rand%
echo lcv is %lcv%
exit /B
I know the goto loop is slower than the for /L loop, but that is the only way to create a breakable infinite loop.
A faster approach is to nest both loop methods: use for /L to iterate a few thousands times and wrap an infinite goto loop around.
An alternative work-around is to make use of the fact that the exit command can break (infinite) for /L loops. But since this also exits the cmd instance the batch file is running in, the loop needs to be placed into a separate cmd instance. Of course the environment is completely separated from the current one. A solution might look like this:
:SUB_bigRandLooper
#echo off
rem // Check for argument, interpret it as jump label if given:
if not "%~1"=="" goto %~1
rem /* Establish explicit `cmd` instance for executing the `for /L` loop;
rem the `for /F` loop implicitly creates the new `cmd` instance for the command
rem it executes, so we do not have to explicitly call `cmd /C` here; the resulting
rem values are echoed by the sub-routine and captured here by `for /F`: */
for /F "tokens=1,2" %%I in ('"%~f0" :bigRandLooperLoop') do (
rem // Assign captured values to environment variables:
set "big-rand=%%I" & set "lcv=%%J"
)
:bigRandLooperWrapup
echo biggest-rand is %big-rand%
echo lcv is %lcv%
exit /B
:bigRandLooperLoop
setlocal EnableDelayedExpansion
set /A lcv=0
for /L %%s in (0,0,1) do (
set big-rand=!random:~-4!
rem /* Explicitly redirect this output to the console window to prevent it from
rem being captured by `for /F` in the main routine too: */
> con echo big-rand is !big-rand!
set /A lcv+=1
if !big-rand! gtr 9900 (
rem // Output found values in order to be able to capture them by `for /F`:
echo !big-rand! !lcv!
rem // Break loop and leave current `cmd` instance:
exit
)
)
endlocal

Compare Variable with multiple values in single If condition

I want to compare variable with multiple values with "OR" condition I batch script rather having multiple if logic.
#echo off
robocopy D:\SourceData E:\DestinationData
If %ErrorLevel% Equ 0 OR 1 OR 2 ( GoTo Success) Else ( GoTo Error)
:Success
Echo Robocopy completed successfully.
Pause
GoTo End
:Error
Echo Robo completed with some error/s.
Paude
:End
Exit
If you want to compare the error level with a certain list:
for %%a in (1 2 3 whatever) do (
if %errorlevel% equ %%a (
goto :Success
)
)
goto :error
ERRORLEVEL is rarely < 0, so you could use:
if %errorlevel% LEQ 2 ( GoTo Success) Else ( GoTo Error)
...and for safety just in case:
if %errorlevel% LEQ -1 ( GoTo Error)
If your values were not consecutive you could just duplicate the if lines without the else (not very good but would work)
If you know all the possible values you can just do
goto branch%ERRORLEVEL%
and define
:branch0
:branch1
:branch2
...and so on.
(this is more or less directly lifted from IF online help, I learned a lot with the /? switch of commands like that)

windows batch errorlevel with if

In the below script even if errorlevel is 0, Its going to if condition "if errorlevel 1"
#echo off
if exist servers.txt goto :continue
echo servers.txt file is missing
exit
:continue
set instance=%username:~2%
setlocal enabledelayedexpansion
for /f "delims=" %%i in (servers.txt) do (
pushd \\%%i\D$\%instance%\Hyperion\oracle_common 2>nul
if not errorlevel 1 (
echo %%i
echo **********************************
set ORACLE_HOME=!CD!
echo ORACLE_HOME is !ORACLE_HOME!
D:
FOR /D /r D:\%instance%\Hyperion %%a in ("jdk160_*") DO CD %%a
set JAVA_HOME=!CD!
echo JAVA_HOME is !JAVA_HOME!
echo D:\%instance%\Hyperion\oracle_common\oui\bin\setup.exe -jreLoc !JAVA_HOME! -silent -attachHome ORACLE_HOME=!ORACLE_HOME! ORACLE_HOME_NAME="REMOTE_EPM"
D:\%instance%\Hyperion\oracle_common\oui\bin\setup.exe -jreLoc !JAVA_HOME! -silent -attachHome ORACLE_HOME=!ORACLE_HOME! ORACLE_HOME_NAME=REMOTE_EPM
echo error code is:%errorlevel%
if errorlevel 2 (
echo unable to attach remote server %%i ORACLE_HOME to inventory
pause
exit
)
cd D:\%instance%\Hyperion\oracle_common\OPatch
if errorlevel 1 (
echo Failed to locate OPatch location D:\%instance%\Hyperion\oracle_common\OPatch
pause
exit
)
echo current: !CD!
opatch.bat lsinv | find "applied on"
D:\%instance%\Hyperion\oracle_common\oui\bin\setup.exe -jreLoc !JAVA_HOME! -silent -detachHome ORACLE_HOME=!ORACLE_HOME! ORACLE_HOME_NAME="REMOTE_EPM"
if errorlevel 1 (
echo Error: unable to detach remote server %%i ORACLE_HOME from inventory
pause
exit
)
popd
pause
) else (
echo ORACLE_HOME is Not found: \\%%~i\D$\%instance%\Hyperion\oracle_common
)
pause
)
endlocal
Output is:
vmhodwbrep9.oracleoutsourcing.com
**********************************
ORACLE_HOME is Y:\pwbre7\Hyperion\oracle_common
JAVA_HOME is D:\pwbre7\Hyperion\jdk160_35
D:\pwbre7\Hyperion\oracle_common\oui\bin\setup.exe -jreLoc D:\pwbre7\Hyperion\jdk160_35 -silent -attachHome ORACLE_HOME=Y:\pwbre7\Hyperion\oracle_common ORACLE_HOME_NAME="REMOTE_EP
M"
error code is:0
unable to attach remote server vmhodwbrep9.oracleoutsourcing.com ORACLE_HOME to inventory
Press any key to continue . . .
Unless command extensions are enabled, you cannot easily access ERRORLEVEL in an echo statement.
Also keep in mind that you must check your conditions in reverse because...:
IF ERRORLEVEL 1 ....
checks to see if ERRORLEVEL is greater than or equal to one. So, a series of tests would be:
IF ERRORLEVEL 5 ....
IF ERRORLEVEL 4 ....
IF ERRORLEVEL 3 ....
IF ERRORLEVEL 2 ....
IF ERRORLEVEL 1 ....
Finally, recognize that in an IF statement, %errorlevel% is *not* the same asERRORLEVEL`. You don't try it this way, but another answer does.
change
echo error code is:%errorlevel%
if errorlevel 2 (
to
call echo error code is:%%errorlevel%%
if errorlevel 2 (
OR, preferably since you have invoked delayedexpansion,
echo error code is:!errorlevel!
if errorlevel 2 (
With your current code, the entirity from
if not errorlevel 1 (
to the single ) before the endlocal line is one block statement.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, since the block starts with
if not errorlevel 1 (
then %errorlevel% will be replaced by the value of errorlevel at the time the if is encountered, that is 0, so your echo will be replaced by echo error code is:0
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var. CALL ECHO %%errorlevel%% displays, but sadly then RESETS errorlevel.
note that last statement
CALL ECHO %%errorlevel%%` displays, but sadly then RESETS errorlevel.
So your errorlevel would now be displayed correctly, but would be reset to 0 by the call.

Resources