Windows Batch:Nested for loop counter not working - batch-file

I'm a novice at batch and been trying to get this to work properly but can't figure out what I'm doing wrong. The counter doesn't increment for some reason?
#echo off
set local enabledelayedexpansion
set /a counter=0
for /F "delims=" %%a in ('dir /b/ad/o "C:\Sources"') do (
for /F "delims=" %%i in ('dir /b/ad/o "C:\Sources\%%a"') do (
set a[%counter]=%%i
set /a counter=counter+1
echo value of counter is: %counter%
)
)
echo array 0 is: %a[0]%
echo array 1 is: %a[1]%

The SET LOCAL should really be SETLOCAL. It's a single command. Also the nested variables should be refernced with !var! and not %var%. If you use %var% it will use the outer scope (and not work correctly).
https://ss64.com/nt/delayedexpansion.html
#ECHO OFF
REM "SETLOCAL" not "SET LOCAL"
SETLOCAL enabledelayedexpansion
SET counter=0
FOR /L %%a IN (1,1,10) DO (
FOR /L %%i IN (1,1,10) DO (
REM USE "!" instead of "%"
SET X[!counter!]=%%i
SET /a counter=!counter! +1
echo value of counter is: !counter!
)
)
ECHO array 0 is: %X[0]%
ECHO array 1 is: %X[1]%
BTW, if you want to have your variables just scoped to your batchfile you should end your script with ENDLOCAL

Related

Batch script - Wrong If syntax

I am trying to check the text in my created array, if I am not using "if" every thing works and i can use "echo" but when I add the "if" command I get "wrong syntax"
#echo off
setlocal ENABLEDELAYEDEXPANSION
set i=0
for /f "delims= " %%a in ('command') do (
set /A i+=1
set list[!i!]=%%~a
)
set Filesx=%i%
rem Display array elements
for /L %%i in (1,1,%Filesx%) do (
if list[%%i] =="some ttext"
echo !list[%%i]!
)
I would consider changing your script accordingly:
#Echo Off
SetLocal EnableDelayedExpansion
Set "i=0"
For /F %%A In ('command') Do (Set/A i+=1
Set list[!i!]=%%~A)
Rem Display array elements
For /L %%A In (1,1,%i%) Do If /I "!list[%%A]!"=="some ttext" Echo !list[%%A]!
Pause
In your script you needed to change if list[%%i] to If /I "!list[%%i]!"
#echo off
setlocal ENABLEDELAYEDEXPANSION
set i=0
for /f "delims= " %%a in ('command') do (
set /A i+=1
set list[!i!]=%%~a
)
set Filesx=%i%
rem Display array elements
for /L %%i in (1,1,%Filesx%) do (
if /i "!list[%%i]!" =="some ttext" (
echo !list[%%i]!
)
)
An if statement requires an exact match (for == or equ) so if you quote one side, you need to quote the other.
also, the action for the if-true condition must be on the same physical line as the if

need help getting batch file to read input but it works fine on the first part strangely

so heres my code the first part executes perfectly but the second doesnt it just displays each variable as blank. im not sure why as its formatted the same also would it be possible to put the :readprofiles part in a working for variable?:
purpose of the program: to list out a directory as profiles along with numbered choices to select. basically a menu.
#echo off
setlocal enabledelayedexpansion
set Counter=1
for /f "DELIMS=" %%i in (test.txt) do (
set "Line_!Counter!=%%i"
set /a Counter+=1
)
set /a NumLines=Counter - 1
:: this part is a test
echo %Line_1%
echo %Line_2%
echo %Line_3%
echo %Line_4%
echo %Line_5%
echo %Line_6%
:: end test
set Counter=1
:readprofiles
if %Counter%==%NumLines% goto pause
echo %Counter%. %Line_!Counter!%
set /a Counter+=1
goto readprofiles
:pause
pause
#echo off
setlocal enabledelayedexpansion
set Counter=0
for /f "DELIMS=" %%i in (test.txt) do (
set /a Counter+=1
set "Line_!Counter!=%%i"
)
For /L %%C in (1,1,%Counter%) Do echo %%C. !Line_%%C!
Pause

Extract a specific portion from a string(filename) using batch file

I am trying to extract a portion of all the filenames(pdf files) in the current directory.
The length of filenames vary except for the last portion(datetime and extension) which will always be 16 characters. The remaining part will always have different lengths. Even the portion I require may have varying lengths.
I tried using lastIndexOf function obtained here.
filename eg : academyo-nonpo-2582365-082416051750.pdf
I want to extract the section in Bold.
I tried trimming the last 17 characters(this portion will always have a fixed length.) first and then tried to obtain the last Index Of '-'(since the fist portion can have variable character length.) and trim the characters until that position, which should return the required portion of the filename.
#echo off
Setlocal enabledelayedexpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call :lastindexof !File! - lio2
Set "File=!File:~%lio%!"
)
Pause&Exit
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal enableDelayedExpansion
set "str=%~1"
set "p=!str:%~2=&echo.!"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
set argv=original
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
set "str=A!%%~2!"%\n%
echo -1
)
setlocal DisableDelayedExpansion
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
%$strlen% strlen,str
%$strlen% plen,last_part
%$strlen% slen,splitter
set /a lio=strlen-plen-slen
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b
The reference of the variable passed to the function as the 3rd parameter doesn't seem to be returning the required value.
I dunno what is wrong here.
To get the section in bold then:
Example#
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~-20,7!"
Echo=!File!%%~x#)
Pause
Okay what about?
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~,-13!"
Call :Sub "!File:-=\!%%~x#")
Pause
:Sub
Echo=%~nx1
To extract the portion in between the last hyphen and the next-to-last one, you could use the following script (provide the strings/files as command line arguments):
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "SEP=-"
for %%A in (%*) do (
set "ITEM=%%~A"
set "PREV="
if defined ITEM (
for %%B in ("!ITEM:%SEP%=" "!") do (
set "PREV=!PART!"
set "PART=%%~B"
)
if defined PREV (
echo(!PREV!
)
)
)
endlocal
exit /B
This approach basically replaces every - by the standard cmd tokenisation character SPACE and iterates through the resulting string using a standard for loop (no /F option). The currently iterated part is stored in variable PART, whose content is first copied into PREV to gain a delay of one loop iteration. So the next-to-last portion is finally stored in PREV.
Note that this script might return unexpected results in case the strings/files contain exclamation marks because of delayed expansion.
Have a look on this answer. Thought is to first count the number of tokens (you still do have to trim the string before this) and then get the last token.
In the first loop where it says "tokens=1*" , you have to edit it to the following: "tokens=1* delims=-" and in the second loop add delims=- as well after %i%. It should be looking like this in total with your script:
#echo off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call:subfunction !File! - lio2
Set "File=!File:~%lio%!"
)
:subfunction
set var1=%1
set var2=%var1%
set i=0
:loopprocess
for /F "tokens=1* delims=-" %%A in ( "%var1%" ) do (
set /A i+=1
set var1=%%B
goto loopprocess )
for /F "tokens=%i% delims=-" %%G in ( "%var2%" ) do set last=%%G
echo %last%
REM do what you want with last here!
I tested it and it seems to be working correctly even with something like ac-ade-myo-n-on-po-15482729242321654-082416051750.pdf, however after finishing correctly, it give an error message one time with a syntax error I could not find...
If you can ignore that error (everything else works), this might help.

Batch File - loop incrementing count in value not displaying correctly

I'm trying to read a file and output the lines of data into registry keys. The data collection works, but I don't understand the syntax required to increment the string values in the last loop.
#echo OFF
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq skip=1 delims=" %%a in (`"findstr /n ^^ C:\GetSID.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!" This removes the prefix
echo(!var:~76,63!>>C:\SIDoutput.txt
goto :EndLoop
)
:EndLoop
set /p SID= <C:\users\paintic\SIDoutput.txt
set KEY_NAME="HKEY_USERS\!SID!\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts"
set Counter=1
for /f %%x in (C:\users\paintic\Networkprinters.txt) do (
set "Line_!Counter!=%%x"
set /a Counter+=1
if !Counter!==3 (Echo %line_counter%)
)
set /a counter2=!counter!-3
set counter=1
The part below is what I can't get to work. I'm trying to write LINE_1, LINE_2 and LINE_3 values from the previous loop to increment via the loop below. So VALUENAME should equal LINE_1, TYPE should = LINE_2's value and DATA should = LINE_3 on the first run and keep going up by 1 until the loop finishes (end of the file read)
`for /L %%i in (1,1,%counter2%) do (
set ValueName=%Line_!counter!%
set /a counter+=1
set Type=%Line_!counter!%
set /a Counter+=1
set Data=%Line_!counter!%
set /a Counter+=1
echo !ValueName!
echo !Type!
echo !Data!
REG ADD %KEY_NAME% /v !ValueName! /t !Type! /d !Data! /f
)
ENDLOCAL
Pause`
On searching for errors in batch file it is always helpful to use in first line #echo on or remove #echo off or comment this line with rem to see what cmd.exe really executes.
Command line interpreter fails on lines with set VariableName=%Line_!counter!% as the interpreter does not know what to expand first. I think it is not possible to create dynamically the name of an environment variable and reference next the value of this environment variable. This approach most likely does not work ever.
However, what you want to achieve can be done much easier directly in second loop as the following example demonstrates:
#echo off
setlocal EnableDelayedExpansion
rem Create data for demo example.
set "KEY_NAME=HKEY_USERS\S-1-5-20\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts"
echo TestValue>"%TEMP%\Networkprinters.txt"
echo REG_SZ>>"%TEMP%\Networkprinters.txt"
echo Sample Data>>"%TEMP%\Networkprinters.txt"
echo AnotherValue>>"%TEMP%\Networkprinters.txt"
echo REG_DWORD>>"%TEMP%\Networkprinters.txt"
echo ^1>>"%TEMP%\Networkprinters.txt"
rem Now the loop follows which reads the data from the file line
rem by line and build the line for using command "reg.exe" to
rem add the data to registry of the user with the defined SID.
set Counter=1
for /f "usebackq delims=" %%x in ("%TEMP%\Networkprinters.txt") do (
if "!Counter!"=="1" (
set "ValueName=%%x"
) else if "!Counter!"=="2" (
set "ValueType=%%x"
) else (
set "ValueData=%%x"
rem Echo the command instead of really executing "reg.exe".
echo reg.exe ADD %KEY_NAME% /v "!ValueName!" /t !ValueType! /d "!ValueData!" /f
set Counter=0
)
set /a Counter+=1
)
rem Delete the text file created for demo example.
del "%TEMP%\Networkprinters.txt"
endlocal
This solution is much easier than what you have tried and can be maybe even more simplified.

How to set a variable inside a loop for /F

I made this code
dir /B /S %RepToRead% > %FileName%
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
echo %%a is working fine but echo %z% returns "echo disabled".
I need to set a %z% because I want to split the variable like %z:~7%
Any ideas?
There are two methods to setting and using variables within for loops and parentheses scope.
setlocal enabledelayedexpansion see setlocal /? for help. This only works on XP/2000 or newer versions of Windows.
then use !variable! instead of %variable% inside the loop...
Create a batch function using batch goto labels :Label.
Example:
for /F "tokens=*" %%a in ('type %FileName%') do call :Foo %%a
goto End
:Foo
set z=%1
echo %z%
echo %1
goto :eof
:End
Batch functions are very useful mechanism.
You probably want SETLOCAL ENABLEDELAYEDEXPANSION. See https://devblogs.microsoft.com/oldnewthing/20060823-00/?p=29993 for details.
Basically: Normal %variables% are expanded right aftercmd.exe reads the command. In your case the "command" is the whole
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
loop. At that point z has no value yet, so echo %z% turns into echo. Then the loop is executed and z is set, but its value isn't used anymore.
SETLOCAL ENABLEDELAYEDEXPANSION enables an additional syntax, !variable!. This also expands variables but it only does so right before each (sub-)command is executed.
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
This gives you the current value of z each time the echo runs.
I struggeld for many hours on this.
This is my loop to register command line vars.
Example : Register.bat /param1:value1 /param2:value2
What is does, is loop all the commandline params,
and that set the variable with the proper name to the value.
After that, you can just use
set value=!param1!
set value2=!param2!
regardless the sequence the params are given. (so called named parameters).
Note the !<>!, instead of the %<>%.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%P IN (%*) DO (
call :processParam %%P
)
goto:End
:processParam [%1 - param]
#echo "processparam : %1"
FOR /F "tokens=1,2 delims=:" %%G IN ("%1") DO (
#echo a,b %%G %%H
set nameWithSlash=%%G
set name=!nameWithSlash:~1!
#echo n=!name!
set value=%%H
set !name!=!value!
)
goto :eof
:End
Simple example of batch code using %var%, !var!, and %%.
In this example code, focus here is that we want to capture a start time using the built in variable TIME (using time because it always changes automatically):
Code:
#echo off
setlocal enabledelayedexpansion
SET "SERVICES_LIST=MMS ARSM MMS2"
SET START=%TIME%
SET "LAST_SERVICE="
for %%A in (%SERVICES_LIST%) do (
SET START=!TIME!
CALL :SOME_FUNCTION %%A
SET "LAST_SERVICE=%%A"
ping -n 5 127.0.0.1 > NUL
SET OTHER=!START!
if !OTHER! EQU !START! (
echo !OTHER! is equal to !START! as expected
) ELSE (
echo NOTHING
)
)
ECHO Last service run was %LAST_SERVICE%
:: Function declared like this
:SOME_FUNCTION
echo Running: %1
EXIT /B 0
Comments on code:
Use enabledelayedexpansion
The first three SET lines are typical
uses of the SET command, use this most of the time.
The next line is a for loop, must use %%A for iteration, then %%B if a loop inside it
etc.. You can not use long variable names.
To access a changed variable such as the time variable, you must use !! or set with !! (have enableddelayexpansion enabled).
When looping in for loop each iteration is accessed as the %%A variable.
The code in the for loop is point out the various ways to set a variable. Looking at 'SET OTHER=!START!', if you were to change to SET OTHER=%START% you will see why !! is needed. (hint: you will see NOTHING) output.
In short !! is more likely needed inside of loops, %var% in general, %% always a for loop.
Further reading
Use the following links to determine why in more detail:
Difference between %variable% and !variable! in batch file
Variable usage in batch file
To expand on the answer I came here to get a better understanding so I wrote this that can explain it and helped me too.
It has the setlocal DisableDelayedExpansion in there so you can locally set this as you wish between the setlocal EnableDelayedExpansion and it.
#echo off
title %~nx0
for /f "tokens=*" %%A in ("Some Thing") do (
setlocal EnableDelayedExpansion
set z=%%A
echo !z! Echoing the assigned variable in setlocal scope.
echo %%A Echoing the variable in local scope.
setlocal DisableDelayedExpansion
echo !z! &rem !z! Neither of these now work, which makes sense.
echo %z% &rem ECHO is off. Neither of these now work, which makes sense.
echo %%A Echoing the variable in its local scope, will always work.
)
set list = a1-2019 a3-2018 a4-2017
setlocal enabledelayedexpansion
set backup=
set bb1=
for /d %%d in (%list%) do (
set td=%%d
set x=!td!
set y=!td!
set y=!y:~-4!
if !y! gtr !bb1! (
set bb1=!y!
set backup=!x!
)
)
rem: backup will be 2019
echo %backup%
Try this:
setlocal EnableDelayedExpansion
...
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
You can use a macro if you access a variable outside the scope
#echo off
::Define macro
set "sset=set"
for /l %%a in (1,1,4) do (
::set in loop
%sset% /a "x[%%a]=%%a*%%a"
if %%a equ 4 (
:: set in condition
%sset% "x[%%a]=x Condition"
%sset% "y=y Condition"
)
)
echo x1=%x[1]% x2=%x[2]% x3=%x[3]% x4=%x[4]% y=%y%
:: Bonus. enableDelayedExpansion used to access massive from the loop
setlocal enableDelayedExpansion
echo Echo from the loop
for /l %%a in (1,1,4) do (
::echo in one line - echo|set /p =
echo|set /p "=x%%a=!x[%%a]! "
if %%a equ 4 echo y=%y%
)
pause
I know this isn't what's asked but I benefited from this method, when trying to set a variable within a "loop". Uses an array. Alternative implementation option.
SETLOCAL ENABLEDELAYEDEXPANSION
...
set Services[0]=SERVICE1
set Services[1]=SERVICE2
set Services[2]=SERVICE3
set "i=0"
:ServicesLoop
if defined Services[%i%] (
set SERVICE=!Services[%i%]!
echo CurrentService: !SERVICE!
set /a "i+=1"
GOTO :ServicesLoop
)
The following should work:
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('type %FileName%') do (
set "z=%%a"
echo %z%
echo %%a
)

Resources