1

I want to iterate though a batch-array:

SET DEF_APP[0].type=.html
SET DEF_APP[0].name=Firefox
SET DEF_APP[1].type=.htm
SET DEF_APP[1].name=Firefox
SET DEF_APP[2].type=.doc
SET DEF_APP[2].name=LibreOffice
SET DEF_APP[3].type=.docx
SET DEF_APP[3].name=LibreOffice

SET I=0
:APP_LOOP 
IF DEFINED DEF_APP[%I%].type ( 
   ECHO %DEF_APP[%I%].type% %I%
   ECHO %DEF_APP[%I%].name% %I%
   SET /A I=I+1
   GOTO :APP_LOOP 
)
ECHO "Length is" %I%
ECHO %DEF_APP[1].type% %I%
ECHO %DEF_APP[1].name% %I%

PAUSE
EXIT

While %I% is printed out as 1,2,3,4 it seems, that %I% can't be used as the index of the array. Printout is:

I 0
I 0
I 1
I 1
I 2
I 2
I 3
I 3
"Length is" 4
.htm 4
Firefox 4
Drücken Sie eine beliebige Taste . . .

I wonder, why the printout from the array is always "I" (no error or something)? In other examples (like in Tutorialspoint in the example below) it should work. They are using a "CALL ECHO %%..."

set Arr[0] = 1 
set Arr[1] = 2 
set Arr[2] = 3 
set Arr[3] = 4 
set "x = 0" 
:SymLoop 

if defined Arr[%x%] ( 
   call echo %%Arr[%x%]%% 
   set /a "x+=1"
   GOTO :SymLoop 
)
echo "The length of the array is" %x%

When I print out

ECHO %DEF_APP[1].name% %I%

(like the last two echoes), it prints out the correct value "Firefox 1". When I print do

SET I=1
ECHO %DEF_APP[%1%].name% %I%

it prints out "I 1". Can it be, that the index %I% is overgiven as char instead of int? And why is the letter "I" printed?

  • 1
    `%DEF_APP[%I%].type%` are two variables with a string in between: first variable `%DEF_APP[%` (empty), string `I`, second variable `%].type%` (also empty). Read about [delayed expansion](https://stackoverflow.com/questions/30282784/variables-are-not-behaving-as-expected/30284028#30284028) – Stephan Jan 26 '20 at 14:43
  • see also https://stackoverflow.com/questions/10166386/arrays-linked-lists-and-other-data-structures-in-cmd-exe-batch-script/59680701#59680701 – T3RR0R Jan 26 '20 at 15:00
  • 1
    You are not the first, who fell into the famous "delayed expansion trap" (and you won't be the last). :-) Thanks for your help, your example is perfectly explained, Stafan. – Martin Schmitz Jan 26 '20 at 21:04

2 Answers2

2

The key to understand the expansion or why it fails, is the order of the three different expansion phases.

First the percent expansion occurs.
All variables between percent signs are expanded here. It doesn't guess variable names. It simply take the chars between percent signs. These phase expands variables BEFORE analyzing is executed, this is important especially for code blocks.

Then For variables (they uses also percent signs) are expanded. And the last expansion is the delayed expansion (must be enabled)

If the line is prefixed with a CALL command, then a fourth expansion round starts. It's a percent expansion.

To your array problem:

SET I=1
ECHO %DEF_APP[%I%].name% %I%

Can't work, as the percent expansion tries to expand DEF_APP[ and ].name

You could use

Echo !DEF_APP[%I%].name! %I%

Now the %I% expands and then the DEF_APP[1].name

jeb
  • 78,592
  • 17
  • 171
  • 225
1

Let me share my way of doing this, and, I am not the author of that method. This is the safe way to define/use arrays in cmd/bat.

I saw this on dostips.com /by @Aacini and after here /by @user6811411 (removed user), some time ago. The original layout code is some like:

Set "var=%var:,="&Set /a i+=1&Set "var[!i!]=%

I prefer replace [] to one _

Also, change i+= to _i+=1+0, no need predefined command: set i=0

But it always works, and requires enabledelayedexpansion variables

@echo off && setlocal enabledelayedexpansion && title <nul && title ...\%~nx0

rem :: define a variable for array_layout ::
set "_def_app.type=.html,.htm,.doc,.docx"
set "_def_app_.name=firefox,libreoffice,libreoffice"

rem :: replace/incrementing delimiter by a set /a_i+1%% :: 
rem :: (miracle loop in set new variable _var_+i/index) ::
set "_def_app.type_0=%_def_app.type:,="&set/a _i+=1+0&set "_def_app.type_!_i!=%"
set "_def_app_.name_0=%_def_app_.name:,="&set/a _k+=1+0&set "_def_app_.name_!_k!=%"

rem :: use a for loop to echo/use the index array value i/I ::
echo/ && for /l %%i in (0 1 !_i!)do for /f tokens^=* %%I in ('echo/!_def_app.type_%%i!
')do echo/  %%I == ^^!_def_app.type_%%%i^^!

rem :: use a for loop to echo/use the indesx array value k/K ::
echo/ && for /l %%k in (0 1 !_k!)do for /f tokens^=* %%K in ('echo/!_def_app_.name_%%k!
')do echo/  %%K == ^^!_def_app_.name_%%%k^^!

rem :: get total index + 1 = positions
echo/ && for %%s in (i,k)do call set /a "_len_%%s=!_%%s!+1"

echo/  Index of [^^!_def_app.type^^!] is: 0-!_i!
echo/ Length of [^^!_def_app.type^^!] is: !_len_i!

echo/ && echo/  Index of [^^!_def_app_.name^^!] is: 0-!_k!
echo/ Length of [^^!_def_app_.name^^!] is: !_len_k!

%%_APPDIR__%timeout.exe -1 & endlocal & goto :EOF


  • Outputs:

  .html  == !_def_app.type_0!
  .htm  == !_def_app.type_1!
  .doc  == !_def_app.type_2!
  .docx  == !_def_app.type_3!

  firefox  == !_def_app_.name_0!
  libreoffice  == !_def_app_.name_1!
  libreoffice  == !_def_app_.name_2!

  Index of [!_def_app.type!] is: 0-3
 Length of [!_def_app.type!] is: 4

  Index of [!_def_app_.name!] is: 0-2
 Length of [!_def_app_.name!] is: 3

Press any key to continue...

  • Same code without comments...
@echo off && setlocal enabledelayedexpansion && title <nul && title ...\%~nx0

set "_def_app.type=.html,.htm,.doc,.docx"
set "_def_app_.name=firefox,libreoffice,libreoffice"

set "_def_app.type_0=%_def_app.type:,="&set/a _i+=1+0&set "_def_app.type_!_i!=%"
set "_def_app_.name_0=%_def_app_.name:,="&set/a _k+=1+0&set "_def_app_.name_!_k!=%"

echo/ && for /l %%i in (0 1 !_i!)do for /f tokens^=* %%I in ('
echo/!_def_app.type_%%i!')do echo/  %%I == ^^!_def_app.type_%%%i^^!

echo/ && for /l %%k in (0 1 !_k!)do for /f tokens^=* %%K in ('
echo/!_def_app_.name_%%k!')do echo/  %%K == ^^!_def_app_.name_%%%k^^!

echo/ && for %%s in (i,k)do call set /a "_len_%%s=!_%%s!+1"

echo/  Index of [^^!_def_app.type^^!] is: 0-!_i!
echo/ Length of [^^!_def_app.type^^!] is: !_len_i!

echo/ && echo/  Index of [^^!_def_app_.name^^!] is: 0-!_k!
echo/ Length of [^^!_def_app_.name^^!] is: !_len_k!

timeout -1 & endlocal & goto :EOF

Io-oI
  • 2,514
  • 3
  • 22
  • 29
  • 1
    How does this answer the question? – DavidPostill Jan 26 '20 at 17:51
  • @DavidPostill Thank you for your comment. This intents to showing how to set/use/print variable in array, and how to handle index, positions value. Understand, my English have no support here/now to help-me to explain more, have you some suggest? – Io-oI Jan 26 '20 at 18:00
  • 1
    @DavidPostill, it might not be the solution, but it shows me my error (like the comments above). For me it was very helpful. – Martin Schmitz Jan 26 '20 at 21:24