Jumping back to label inside loop? [duplicate] - windows

This question already has answers here:
Loop through input parameters and array? [duplicate]
Arrays, linked lists and other data structures in cmd.exe (batch) script
(11 answers)
Closed 2 years ago.
I need to loop through the list of files set through the command line, and loop through a local array.
No knowing of a better way, I tried using labels, but cmd doesn't behave as expected:
#echo off
if "%~1"=="" GOTO PARAM
setlocal enableextensions enabledelayedexpansion
set colors[0]="<style>yellow</style>"
set colors[1]="<style>blue</style>"
set COUNTER=0
for %%f in ("%1") DO (
echo Handling %%f
echo !COUNTER!
:LOOP
IF !COUNTER!=="0" GOTO CASE_YELLOW
IF !COUNTER!=="1" GOTO CASE_BLUE
IF !COUNTER! GTR 6 GOTO END
set /a COUNTER +=1
)
GOTO END
:CASE_YELLOW
ECHO Case YELLOW
GOTO LOOP
:CASE_BLUE
ECHO Case BLUE
GOTO LOOP
:PARAM
echo Usage : %0 myfile.xml/*.xml
:END
ECHO Done.
Here's the output using "myscript.bat file*.xml":
Handling file1.xml
0
Handling fileé.xml
1
Done.
Thank you.

Thankfully you put enough info in your code commenting to kinda see where you were going to with the arguments.
that said, if you want to do something when a condition is met in a loop, and continue with the loop after doing it, you simply need to change to calling your sub Functions, and then end of file which will return tou to your place in the loop.
this should do the needful
#(setlocal enableextensions enabledelayedexpansion
echo off
set "colors[0]=<style>yellow</style>"
set "colors[1]=<style>blue</style>"
set /a "COUNTER=0"
SET "SRC=%~1"
)
IF /I "%~1" EQU "" (
Call :PARAM
) ELSE (
CALL :Main
)
( ENDLOCAL
CALL :End
EXIT /B 0
)
:Main
For %%f in ("%SRC%") DO (
IF !COUNTER! < 6 (
echo Handling %%f
echo !COUNTER!
CALL ECHO.IN MAIN LOOP COLOR= "%%Colors[%counter%]%%"
IF !COUNTER!==0 CALL :CASE_YELLOW
IF !COUNTER!==1 CALL :CASE_BLUE
set /a "COUNTER+=1"
) ELSE (
echo.exiting.
GOTO :EOF
)
)
GOTO :EOF
:CASE_YELLOW
ECHO Case YELLOW
ECHO.In sub function yellow COLOR = "!Colors[%counter%]!"
GOTO :EOF
:CASE_BLUE
ECHO Case BLUE
ECHO.In sub function blue COLOR= "!Colors[%counter%]!"
GOTO :EOF
:PARAM
echo Usage : %0 myfile.xml/*.xml
GOTO :EOF
:END
ECHO Done.
Here's the out
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.

How to use % or ! in batch script

I write script like this:
#ECHO OFF
setlocal EnableDelayedExpansion
set "remove=ABC"
echo. %remove%
Set FILENAME="456_789_ABC00011092_789_EFGHIK_56893.mpg"
for %%a in (%FILENAME:_=" "%) do (
set TEN=%%a
echo. %AB%
set "remove_1=ABC"
echo. %remove_1%
Set _TEN=!TEN:%remove%=!
echo. %_TEN%
Set i=0
IF !_TEN! NEQ !TEN! (
set /A i+=1
set "String[!i!]=%%~a"
)
)
pause
exit
Why echo. %AB% echo. %remove_1% result is
I replace % by !. It's work fine but command Set _TEN=!TEN:!remove_1!=! not run
Edit - (from the additional question currently posted as an answer)
When I use FindStr command like this:
for %%a in (%FILENAME:_=" "%) do (
echo %%a | findstr /I /R /C:"ABC" >nul
ECHO %errorlevel%
if "%errorlevel%" equ "0" (
set /A i+=1
set "String[!i!]=%%~a"
)
)
Why errorlevel always = 0
%AB% has not been defined within your posted script, so as it has no value will not be echoed, you will just get an empty line due to the . after echo. Because remove_1 is being set within the loop, (code block), you should be using the delayed expansion syntax, Echo !remove_1!. It is the same for echo. %_TEN%, i.e. Echo !_TEN!, and would have been Echo !AB! had it previously been defined. In order to get the double expansion needed to Set your _TEN variable, you could use a pseudo Call:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Set "TEN=%%A"
Echo. !AB!
Set "remove_1=ABC"
Echo !remove_1!
Call Set "_TEN=!TEN:%%remove_1%%=!"
Echo !_TEN!
Set "i=0"
If "!_TEN!" NEq "!TEN!" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Pause
Exit /B
In your second related question, initially posted as an answer and now added as an edit to your original question; because the error level is being set within the loop, (code block), you should be using the delayed expansion syntax, !errorlevel!
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul
Echo !errorlevel!
If "!errorlevel!"=="0" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B
Or if you don't need to Echo each error level to the screen, you can use a conditional statement &&:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul && (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B

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

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.

how to compare numeric variables in batch files

why doesn't this work?
SET FIRST=""
SET COUNT=0
FOR %%F IN (dir *.png) DO (
IF %COUNT% NEQ 0 GOTO _skip
SET FIRST=%%F
:_skip
ECHO "%%F",
SET /A COUNT=COUNT+1
)
It sets FIRST to the last *.png because the IF condition fails, because COUNT - although is incremented by set /A, the IF %COUNT% doesn't ever work. Very frustrating.
Don't need to count, just do goto skip after echo line.
#echo off
for /f "delims=" %%f in ('dir /b *.png') do (
rem :: you can use "echo %%f" instead of "set first=%%f"
set first=%%f
goto _skip
)
:_skip
echo %first%
you mixing two things to scan folder.
Here is the second way:
#echo off
for %%f in (*.png) do (
set first=%%f
goto _skip
)
:_skip
echo %first%
exit /b 0
If you need absolutely to count, here is the way to skip with count. As stated in comment, you need to enable delayedExpansion
#echo off
set count=1
for %%f in (*.png) do (
set first=%%f
setlocal enabledelayedexpansion
if "!count!"=="1" goto _skip
endlocal
set /a count+=1
)
:_skip
echo !first!

Resources