Windows Batch script - For /L not working -- simple - windows

need some quick help. This is a university program, everything is working fine except when I call my :forLoop method to iterate through 100 numbers (1,1,100) starting at 1 going by 1 til 100 and doing the iteration % 5 (i%%5). for some reason I cannot get this to work. appreciate any help or direction.
When I echo %%A it is iterating through all the number perfect. When I echo %result% I get a blank "" (nothing inside)
:forLoop
FOR /L %%A IN (1,1,100) DO (
set /A result=%%A %% 2
echo "%%A"
echo "%result%"
)
Correct code is
:forLoop
setlocal ENABLEDELAYEDEXPANSION
FOR /L %%A IN (1,1,100) DO (
set /A result=%%A %% 5
echo !result! >> results.txt
set /A total=!total!+!result!
echo !total!
)

The problem is that the %result% is substituted when the for is read, meaning that it is no longer a variable when the loop is executed. What you need is delayed variable expansion to be enabled and then use ! instead of %:
setlocal ENABLEDELAYEDEXPANSION
:forLoop
FOR /L %%A IN (1,1,100) DO (
set /A result=%%A %% 5
echo "%%A"
echo !result!
)
This is all explained in the help message you get when you run SET /?:
Delayed environment variable expansion is useful for getting around
the limitations of the current expansion which happens when a line
of text is read, not when it is executed. The following example
demonstrates the problem with immediate variable expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since the %VAR% in BOTH IF statements
is substituted when the first IF statement is read, since it logically
includes the body of the IF, which is a compound statement. So the
IF inside the compound statement is really comparing "before" with
"after" which will never be equal. Similarly, the following example
will not work as expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of files in the current directory,
but instead will just set the LIST variable to the last file found.
Again, this is because the %LIST% is expanded just once when the
FOR statement is read, and at that time the LIST variable is empty.
So the actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different
character (the exclamation mark) to expand environment variables at
execution time. If delayed variable expansion is enabled, the above
examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%

Related

Enable/Disable Delayed Expansion in FOR /f loop (batch)

I am still struggling to properly understand the behaviour of Disable/EnableDelayedExpansion...
I want to parse input arguments when calling something like command -a -b -c file such to finally have options=-a -b -c and filename=file.
To do so I use the FOR /f loop:
set "count=0"
set "opts="
set "fl="
set tmpv=
:argloop
for /f tokens^=1^,^*^ delims^= %%a in ("%1") do (
echo.
echo Chosen option is %1
set /a count+=1
echo.
echo Reading %count% is %%a..
set "tmpv=%%a"
rem setlocal enabledelayedexpansion
echo Tmp is %tmpv% after set equal %%variable.
rem endlocal
rem setlocal disabledelayedexpansion
set "tmpv=%tmpv:-=%"
rem setlocal enabledelayedexpansion
echo After removing it writes !tmpv!
rem endlocal
rem setlocal disabledelayedexpansion
if "%tmpv%"=="%%a" (
echo Input does not contain "-"
set "fl=%tmpv%"
echo %fl%
) else (
echo/Options before are %opts%
echo.
if "%opts%"=="" (
echo Options are empty.
set opts=%%a
) else (
set "opts=%opts% %%a"
)
)
if not "%2"=="" (shift & goto:argloop)
)
echo.
echo Finally options are %opts%
set opts=%opts:-=/%
echo Finally options are %opts%
echo File name %fl%
set tmpv=
set count=
goto:end
Output writes:
Chosen option is -a
Reading 1 is -a..
Tmp is after set equal %variable.
After removing it writes
Options before are
Options are empty.
Chosen option is -b
Reading 2 is -b..
Tmp is -= after set equal %variable.
After removing it writes -=
Options before are -a
Chosen option is -c
Reading 3 is -c..
Tmp is = after set equal %variable.
After removing it writes =
Options before are -a -b
Chosen option is flfl
Reading 4 is flfl..
Tmp is = after set equal %variable.
After removing it writes =
Options before are -a -b -c
Finally options are -a -b -c flfl
Finally options are /a /b /c flfl
File name
I had made it working with EnableDelayedExpansion, but not capable of storing final %fl% variable.
But why does it not work this way (without using delayed expansions)??
I will sincerely appreciate whom will try to clarify it in all extents.
The rules really aren't too hard.
You are aware that %var% is resolved to the value of var.
When a loop is parsed, every %var% within that loop is replaced by the THEN-current value of var. This includes pseudovariables like %cd%, %errorlevel% and %random%.
If delayedexpansion is in effect (and it is "in effect" from the setlocal enabledelayedexpansion instruction [start-of-setlocal-bracket] until an endlocal or end-of-file [end-of-setlocal-bracket] is reached) then !var! is resolved to the contents of var at the time that particular instruction (set, echo, etc) is executed.
If delayedexpansion is NOT in effect then !var! is simply that - the literal string !var!.
And one small kink. Any change made to the environment (addition, deletion or variation of variable values) is discarded when a setlocal bracket ends.
So, in all probability, you could display the difference by echoing %var% alongside !var!
and %%x (a metavariable) is always resolved to its current value, regardless of setlocal status.
[After responses]
Since all setlocal enabledelayedexpansion/endlocal brackets are remmed-out in the published code, I'm not surprised at the results.
However, running the published code does not yield the published results - for me, the response was "Reading 0"..."Reading 3".
So, looking at the for loop, I believe it's equivalent to
for /f "tokens=1,* delims=" %%a in ("%1") do (
which in turn is the same as
for /f "delims=" %%a in ("%1") do (
since there are no delimiters, an this is effectively the same as
for %%a in (%1) do (
which does nothing beyond assigning %1 to %%a and making the entire loop one block statement.
So therefore, this code should do the same job - without the kinkiness afforded by setlocal...
#ECHO OFF
SETLOCAL
set "count=0"
set "opts="
set "fl="
set "tmpv="
:argloopn
SET "arg=%1"
IF NOT DEFINED arg GOTO report
SET /a count+=1
ECHO arg %count% is %1 IN variable ARG = "%arg%"
SET "tmpv=%arg%"
rem remove "-"
set "tmpv=%tmpv:-=%"
IF "%tmpv%"=="%arg%" (
echo Input does not contain "-"
set "fl=%tmpv%"
) ELSE (
ECHO Input contains "-" so is option
SET "opts=%opts% %arg%"
)
SHIFT
GOTO argloopn
:report
rem fix-up options since 1st char, if it exists must be a space as [space]newoption is added each time
IF DEFINED options SET "options=%options:~1%"
echo Finally options are %opts%
set opts=%opts:-=/%
echo Finally options are %opts%
echo File name %fl%
echo.
GOTO :EOF
I've assumed that a proper batch-debug environment has been established; hence goto :eof to terminate the batch and an inintial setlocal to preserve the original environment.
When you use the point-click-and-giggle method of executing a batch, the batch window will often close if a syntax-error is found. You should instead open a 'command prompt' and run your batch from there so that the window remains open and any error message will be displayed.
--- BUT ---
In testing, I tried this:
#ECHO OFF
:parasite
setlocal
set "opts="
:loopp
FOR /f "delims=" %%a IN ("%1") DO (
SET "opts=%opts% %%a"
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OPTS was "%opts%" is now "!opts!"
ENDLOCAL
ECHO %%opts%% is "%opts%" and !opts! is "!opts!"
)
SHIFT
IF "%1"=="" GOTO :EOF
GOTO loopp
Which didn't do what I expected it to do - that is, report
%opts% is " -a -b -c file" and !opts! is "!opts!"
Instead, it reported
%opts% is " -a -b -c file" and -a -b -c file is " -a -b -c file"
Which I find puzzling as the !var! is outside the setlocal enabledelayedexpansion/endlocal command-bracket and hence should not have been replaced, in my view.
Seems like an #jeb problem to me... so I'll see whether he's got an explanation...

Windows Batch: Turning DelayedExpansion on/off inside a loop and preserving the value of variables while doing so? [duplicate]

This question already has answers here:
Make an environment variable survive ENDLOCAL
(8 answers)
Closed 3 years ago.
I'm trying to use a FOR loop to read the lines in a text file, but I also need to keep track of some variables and evaluate them. The easiest way to do that is by enabling DelyaedExpansion. Actually, it seems to be the ONLY way as everything else I've tried in relation to variables fails miserably if I don't use it. Unfortunately, this means that if any of the lines of text in the file contain exclamation points, they will be stripped out.
I thought I had found a solution by reading a line of text and putting it into a variable, THEN enabling DelayedExpansion, doing the variable operations, and finally using ENDLOCAL & SET VARIABLE=%VARIABLE% to preserve the value. Unfortunately that doesn't seem to work if the ENDLOCAL statement is inside a loop.
For example;
echo off
for /F "delims=" %%F in (test.txt) do (
set Line=%%F
setlocal enabledelayedexpansion
set /a Count=Count+1
echo !Count! - !Line!
endlocal & set Count=%Count%
)
echo Total: %Count%
Each time the loop repeats, the value of "Count" is reset to zero.
If I move the SETLOCAL command before the FOR command, it will strip any "!" from the text, which is unacceptable.
Please note: The example above is only a small part of a much larger script that does many things with the variables inside the loop. I have boiled the problem down to the bare minimum to make it easy to understand. I need to preserve "!" in text read from a file while also being able to perform multiple variable operations within each loop.
So I either need a way to read text from a file, one line at a time, with DeleyedExpansion enabled AND preserve any "!" in the text, or preserve the value of variables that are defined within the SETLOCAL/ENDLOCAL commands within a loop.
With Help from dbenham and his answer here, There is a Solution that exists for this Scenario.
The key, as Dave has Shown, is in Setting the variables PRIOR to using SetlocalEnableDelayedExpansion so that ! is preserved.
#echo off
Set "count=0"
For /F "delims=" %%F in (test.txt) do (
Call :LineParse "%%~F"
)
REM The Below Loop demonstrates Preservation of the Values
Setlocal EnableDelayedExpansion
For /L %%a in (1,1,!count!) DO (
ECHO(!line[%%a]!
)
Endlocal
pause
exit
:LineParse
Set /a count+=1
Set "Line[%count%]=%~1"
Setlocal EnableDelayedExpansion
ECHO(!Line[%count%]!
ECHO(Total: !count!
(
ENDLOCAL
)
GOTO :EOF
There are still a few characters that will not be parsed as desired with this Method, noted in test.txt:
test.txt
Safe Characters: ! > * & ` ' . ] [ ~ # # : , ; ~ } { ) ( / \ ? > < = - _ + $ |
problem Characters: ^ "" %%
problem examples:
line disappears " from single doublequote
but not "" from escaped doublequote
%% will not display unless escaped. % unescaped Percent Symbols will attempt to expand %
caret doubles ^ ^^ ^^^
Don't need to complicate...
Just replace:
echo/ to set /p
setlocal enabledelayedexpansion to cmd /v /c
#echo off
for /F "delims=" %%F in ('type test.txt')do set /a "Count+=1+0" && (
(echo/ & cmd /v/s/c "set/p "'=!Count! - %%F"<nul")>>".\newfile.txt")
cmd /v /c echo/ Total: !Count! && call set "Count="<nul && goto :EOF

I want to store in a variable (via loop) names of .jpg files in current folder

for BAT file when I write following script I don't get the name of the .jpg files in the folder. What's the error and how it can be achieved?
for /f "tokens=*" %%f in ('dir /b *.jpg') do (
SET newname=%%f
SET front=%newname:~0,6%
echo %front%
)
When a compound statement enclosed in parentheses is to be executed,
the statement is first parsed from the open parenthesis all of the
way to the matching close-parenthesis.
At this time, any %var% is replaced by that var's value from the
environment AT THE TIME IT IS PARSED (ie its PARSE-TIME value.)
THEN if the statement seems valid, it is executed.
There are three common ways of accessing the RUN-TIME value of the
variable (as a FOR loop executes, for instance.)
1/ SETLOCAL ENABLEDELAYEDEXPANSION which switches to a mode where
!var! may be used to access the runtime value of var
2/ CALL set var2=%%var%% to set the value of var2 from the
runtime value of var
3/ Executing a subroutine, internal or external within which %var%
will be the runtime value.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%i IN (1 2 3) DO (
ECHO START of run %%i
ECHO using ^!time^! : !time! - PARSE TIME was %time%
CALL ECHO using CALL %%%%TIME%%%% : %%TIME%%
CALL :report
timeout /t 5
ECHO using ^!time^! : !time!
CALL ECHO using CALL %%%%TIME%%%% : %%TIME%%
CALL :report
ECHO END of run %%i
ECHO.
)
GOTO :eof
:report
ECHO :report says TIME is %TIME%
GOTO :eof
A few items to note:
The instruction
IF ERRORLEVEL n echo errorlevel is n OR GREATER
ALWAYS interprets the RUN-TIME value of ERRORLEVEL
IF SET VAR ALWAYS interprets the RUN-TIME value of VAR
The magic variables like ERRORLEVEL and TIME should never
be SET. If you execute
SET ERRORLEVEL=dumb
then ERRORLEVEL will adopt the value dumb because the current
value in the environment takes priority over the system-assigned value.
This is one option - but ! characters in the filenames will cause issues.
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%f in ('dir /b *.jpg') do (
SET "newname=%%f"
SET "front=!newname:~0,6!"
echo !front!
)
pause

For loop only loops once

I have a very weird error occurring in my Batch script where a For loop is only looping once when it should loop many more times (at least 10 times).
Can you tell me why my For loop is only looping once and how I can fix it?
#ECHO off
CLS
SETLOCAL
SET macroFolder=_PLACE_4DM_FILES_HERE
REM the following for loop only loops once when it should be looping more
REM because theres over 10 *.4dm files in the folder??
for /r ./%macroFolder% %%i in ("*.4dm") do SET "file=%%i" (
echo %file%
REM Note I need to store %%i in a variable so I can edit it later
REM And placing %%i in a variable within the for loop results in
REM the var being empty for some reason. For eg
SET file=%%i
ECHO file is %file%
REM Prints "file is "
)
ECHO.
PAUSE
ENDLOCAL
You have (at least) two problems. The first is that you seem to be mixing the bracketed and non-bracketed form of the for statement:
for /r ./%macroFolder% %%i in ("*.4dm") do SET "file=%%i" (
blah blah
)
In that case, the non-bracketed bit is executed per-loop-iteration but the bracketed bit executes after loop exit. You can see this with:
for /r ./%macroFolder% %%i in ("*.4dm") do echo 1 (
echo 2
)
which outputs (in my case where there are three 4dm files):
1 (
1 (
1 (
2
Your second problem is that %% variable expansion is done before the command executed and the for command is a multi-line one, from the for to the closing ) bracket. Any variables you need expanded, that have been set within the loop, need to be done with delayed expansion.
Start with this:
#SETLOCAL enableextensions enabledelayedexpansion
#ECHO off
CLS
SET macroFolder=4dmfiles
for /r ./%macroFolder% %%i in ("*.4dm") do (
SET file=%%i
ECHO file is !file!
)
ECHO.
PAUSE
ENDLOCAL
and you'll see (for my three files):
file is C:\Users\Pax\Documents\4dmfiles\1.4dm
file is C:\Users\Pax\Documents\4dmfiles\2.4dm
file is C:\Users\Pax\Documents\4dmfiles\3.4dm

command-line 'FOR'

There are 3 files in 'D:\log' folder, which names are 'log1.txt log2.txt log3.txt' respectively. I want to show some infomations by using 'FOR' MS-DOS command.
set /a C=0
for %%I in (log*.txt) do (
echo "%C%--%%I"
set /a C+=1
)
Output strings were like this
0--log101105.txt
0--log101116.txt
0--log101117.txt
But, Expected outputs are like belows not aboves
0--log101105.txt
1--log101116.txt
2--log101117.txt
How to increase value of parameter 'C' while FOR loop?
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set /a c=0
for %%I in (log*.txt) do (
echo "!C!--%%I"
set /a c=!c!+1
)
vYou need to enable "delayed variable expansion" for this.
Check out the help for the SET statemen (SET /?).
It explains exactly your problem

Resources