Batch: Array index returns blank - arrays

In this piece of code I'm trying to make a list fit the screen in WinPE by alternating into two columns. But when I put !modelsvar[%increment%]:~20! in the echo it returns only ~20. Strange because !modelsvar[%%D]:~20! works fine. I've tried many variations of ! and % but no luck. Anyone know if there's is a specific rule that I'm missing?
I have setlocal enabledelayedexpansion enabled
set modelsx=%counter%
set /a counter=0
for /l %%D in (1,2,%modelsx%) do (
set /a counter+=1
set /a increment=!counter!+1
ECHO !counter!. !modelsvar[%%D]:~20! !increment!. !modelsvar[%increment%]:~20!
set /a counter+=1
)

for /l %%D in (1,2,%modelsx%) do (
set /a counter+=1
set /a increment=counter+1
for %%X in (!increment!) do (
ECHO !counter!. !modelsvar[%%D]:~20! !increment!. !modelsvar[%%X]:~20!
)
set /a counter+=1
)
Further details at Arrays, linked lists and other data structures in cmd.exe (batch) script

You're expanding increment immediately within ECHO, that is like %increment%, so the returned value is the one before the for loop executes.
Here is another work-around:
set modelsx=%counter%
set /a counter=0
for /l %%D in (1,2,%modelsx%) do (
set /a counter+=1
set /a increment=!counter!+1
call ECHO !counter!. !modelsvar[%%D]:~20! !increment!. %%modelsvar[!increment!]:~20%%
set /a counter+=1
)

Related

Double Expansion on Array Index Windows Batch

Inside the for loop I'm trying to access the element at index count in CLs (this line of code: echo !!CLs[!count!]!!) , but I'm not sure how to do this. I don't really understand how expansion works in this case, so what you see below it me trying something out of no where.
#ECHO off
setlocal enableextensions enabledelayedexpansion
SET CLs[0]=#
SET /A count = 0
FOR /F "tokens=5" %%I IN ('some command') DO (
echo !!CLs[!count!]!! :: THIS LINE
IF NOT %%I == CLs[!count!] (
SET /A count += 1
SET CLs[!count!]=%%I
)
)
echo The item is %CLs[10]%
endlocal
Thanks
According to the post How does the Windows Command Interpreter (CMD.EXE) parse scripts? (see phase 5), the line echo !!CLs[!count!]!! cannot work, because the opening !! are collapsed to a single !, then !CLs[! is expanded to an empty string (assuming such variable is not defined), then count is returned literally, then !]! is expanded to an empty string and the final ! is dismissed. Or in other words, delayed expansion cannot be nested.
You can use call though to introduce another parsing phase, like this:
call echo %%CLs[!count!]%%
The line IF NOT %%I == CLs[!count!] ( ... ) is wrong, you must expand the right value too. However, call if will not help unfortunately, because if (like for and rem) is a special command that is recognised by the parser earlier than others, like call.
To work around that you can store the value of !count! in a for meta-variable, like %%J, for instance, to introduce another parsing phase, and use !CLs[%%J]! then, like this:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
for %%J in (!count!) do (
echo !CLs[%%J]!
if not "%%I" == "!CLs[%%J]!" (
set /A "count+=1"
set "CLs[!count!]=%%I"
)
)
)
Another yet slower possibility is to put the relevant code into a sub-routine:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
call :SUB !count!
)
goto :EOF
:SUB
echo !CLs[%~1]!
if not "%%I" == "!CLs[%~1]!" (
set /A "count+=1"
set "CLs[%~1]=%%I"
)
goto :EOF
You may also take a look at the post Arrays, linked lists and other data structures in cmd.exe (batch) script about how to deal with such pseudo-arrays.
ECHO ------------- START AT %time%
REM <!-- language: lang-dos -->
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /f "tokens=1,2,3delims=[]=" %%a IN ('set cls[') DO IF /i "%%a"=="cls" (
IF "%%I"=="%%c" (SET /a clscnt[%%b]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
ECHO -------------------------Second way -----------------
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /L %%a IN (0,1,!count!) DO (
IF "%%I"=="!cls[%%a]!" (SET /a clscnt[%%a]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
GOTO :EOF
I used a file named q58209698.txt containing some dummy data for my testing and chose to use the entire data line, having no suitable files where token 5 existed.
Note that as a bonus, I've added clscnt - an array of occurence-counts.
Shown: two separate ways of achieving the aim of finding/counting the unique tokens. Naturally, if the cls array is pre-loaded with the required tokens, then it's basic-programmer's-play to adjust the code to detect/report occurrences of those tokens.
The two methods are similar. In the first, set is used to list the established variables starting cls[. The first if ensures processing only the array-name cls, then either it's a repeat (set prcoessed to a value and increment the occurrences-counter) or it's a new value (when the for...%%a loop ends, processed is still undefined) so record it.
The second way is more direct, using the value of count to specifically interrogate the values in the cls array.

How can I refer to the current index while iterating an array in batch?

When I try iterating the folders with a for each approach I have no access to the current index and I've also failed to manually keep one:
#echo off
set "i=0"
set folders='dir /b /ad'
for /f "eol=: delims=" %%D in (%folders%) do (
:: echo %%D
echo %i%
set /a "i+=1"
)
When I try iterating with a fori approach based on this example I can't even get it working:
#echo off
cls
set "i=0"
:SymLoop
set folders='dir /b /ad'
if defined folders[%i%] (
echo %%folders[%i%]%%
set /a "i+=1"
GOTO :SymLoop
)
I'm aware of my total lack of knowledge on the topic so I'd appreciate any kind of correction and/or advice.
#ECHO OFF
SETLOCAL
#echo off
set /a i=0
set folders='dir /b /ad'
for /f "eol=: delims=" %%D in (%folders%) do (
REM echo %%D
CALL echo %%i%%
CALL SET "folders[%%i%%]=%%D"
set /a i+=1
)
SET fol
ECHO ---------------------------
#echo off
set /a i=0
:SymLoop
set folders='dir /b /ad'
if defined folders[%i%] (
CALL echo %%folders[%i%]%%
set /a "i+=1"
GOTO SymLoop
)
GOTO :EOF
Please refer to endless examples on SO about delayed expansion for simpler ways.
Not a good idea to use ::-comments within a (code block) as it can break the block.
set /a does not ordinarily require "quotes"

I need a format of code that only counts odd lines

test_title.bat
:GET_DOWNLOADS
set Counter=-1
for /f "DELIMS=" %%i in ('type version.txt') do (
set /a Counter+=2
set "Line_!Counter!=%%i"
)
if exist version.txt del version.txt
exit /b
:list_files
call :GET_DOWNLOADS
For /L %%C in (1,2,%Counter%) Do (
:: removing this part makes it work fine
set line=%%C
set /a line+=1
set /a line/=2
:: alternate way doesnt work either
REM set /a line=%line% / 2
:: this part without the math part would be %%C instead of %Line%
echo %line%. !Line_%%C!
)
pause
(made an edit)
the second part isnt working for some reason
it just crashes
if i remove the line that does the math it works fine but instead display 1. 3. 5. 7.
version.txt
everything
0
minecraft
0
steam
0
obs
0
fixed test_list.bat :D
#echo off
setlocal enabledelayedexpansion
set "num=1"
set "counter=0"
for /f "DELIMS=" %%i in (version.txt) do (
set /a num+=1
if "!num!"=="2" (set /a counter+=1&set "line_!counter!=%%i"&set num=0)
)
echo.
For /L %%C in (1,1,%Counter%) Do (echo %%C. !Line_%%C!)
pause
#echo off
setlocal enabledelayedexpansion
set "num=1"
set "counter=0"
for /f "DELIMS=" %%i in (version.txt) do (
set /a num+=1
if "!num!"=="2" (set /a counter+=1&set "line_!counter!=%%i"&echo %%i&set num=0)
)
echo.
set line_1
set line_2
set line_3
pause
Would output:
everything
minecraft
steam
obs
line_1=everything
line_2=minecraft
line_3=steam

Random File Picker Picks Same Value On Each Execution (batch)

I'm trying to launch a random file with this software via a script - the only problem is that this script always selects the same random number on every launch. It's always 41 for me... Any suggestions?
#echo on
setlocal EnableDelayedExpansion
cd C:\Users\User\Documents\Downloads\Nintendo
set n=0
for %%f in (*.*) do (
set /A n+=1
set "file[!n!]=%%f"
)
set /A "rand=%random% * 100 / 32768+1"
"C:\Users\User\Downloads\fceux-2.2.2-win32\fceux.exe" "!file[%rand%]!"
That works for me:
#echo off
setlocal enabledelayedexpansion
c:
cd \windows
for %%f in (*.*) do (
set /A n+=1
set "file[!n!]=%%f"
)
rem change random seed value
for /l %%i in (0,1,%time:~-2%) do set /A tmp=!random!
set /A rand=!random! %% n-1
echo "!file[%rand%]!"

Loop through "array" in batch file to shift elements

I have a batch file that is passed commands in the form of a string array from a Java file. The commands contain something like the following:
String[] commands = {"A",
"B",
"C",
"C:\users\user\Documents",
"C:\users\user\Pictures"}
The commands array is dynamic, as it changes every time the java program is run. In the batch file, I create variables to take the values of the first three elements (A, B, and C in this case). Then I need to shift the directory strings to take up the first three elements of the array. Here is the batch code I have so far:
#echo off
setlocal enableDelayedExpansion
set /A paramCount=0
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[0]%
set argB=%list[1]%
set argC=%list[2]%
set /A old=0
set /A new=!old!+3
for /F "tokens=2 delims==" %%a in ('set list[') do (
echo old=!old!
echo new=!new!
set list[!old!]=!list[%new%]!
echo !list[%old%]!
set /A old=!old!+1
set /A new=!new!+1 )
The problem I am having is with the line set list[!old!]=!list[%new%]!. As you can see, I have delayed expansion enabled. However, the !!'s are needed for the list[...] variable that is emulating an element in an array. However, I believe I need to use delayed expansion for "new" as well. What am I to do in this case? Or perhaps that's not the actual problem? The "old" and "new" variables are incrementing correctly, but the echo !list[%old%]! line returns the same value every time. I expect the same issue exists in that line, with "old"--It should have !'s surrounding it but the !'s are already being used for the list[...] variable. So what happens if you need nested !'s in a statement? Thanks for the aid!
#echo off
setlocal ENABLEDELAYEDEXPANSION
set /A paramCount=-3
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[-3]%
set argB=%list[-2]%
set argC=%list[-1]%
for /F "tokens=2 delims==" %%a in ('set list[-') do SET "%%a="
SET arg
SET list
ENDLOCAL
echo==================
setlocal ENABLEDELAYEDEXPANSION
set /A paramCount=0
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[0]%
set argB=%list[1]%
set argC=%list[2]%
set /A old=0
set /A new=!old!+3
for /F "tokens=2 delims==" %%a in ('set list[') do (
echo old=!old!
echo new=!new!
CALL set list[%%old%%]=%%list[!new!]%%
CALL ECHO(%%list[!old!]%%
set /A old=!old!+1
set /A new=!new!+1
)
SET arg
SET list
GOTO :EOF
This should work for you - the easy way and the hard way.

Resources