Arrays in Batch

Arrays in Batch Files by Jakash3

Written by Jakash3
December 18,2009

It is now possible to create arrays in batch files.
Of course you assign them like this:

set myarray[0]=butter
set myarray[1]=lights
set myarray[2]=orb

But to obtain the value of an array item by specifying
it’s index by a variable would work by calling this function:

REM Example call: call :getitem myarray 1 value
REM After that: 'echo %value%' outputs in this case "lights"
:getitem
set array.name=%1
set array.index=%2
set outputvar=%3
for /f "delims=[=] tokens=1,2,3" %%a in ('set %array.name%[') do (
if %%b==%array.index% set %outputvar%=%%c
)
goto :eof

The above function arguments when calling:
%1 = Name of the array
%2 = Index to get value from
%3 = The name of a variable to output the value to

I made a batch file 'library' to help you accomplish this.
With this you can add new items to the end of arrays, get
their length, or get the value of an item index.

array.bat

@echo off

set array.return=goto :eof
set array.func=%1
set array.func.args=%2 %3 %4 %5 %6 %7 %8 %9
call :%array.func% %array.func.args%
goto :eof


:add
REM Adds a new item at the end of an array
REM Arguments: (
REM name As "Array Name",
REM value As "New value"
REM )
set array.name=%1
set array.value=%2
for /f "delims=[=] tokens=1,2,3" %%a in ('set %array.name%[') do (
set array.index=%%b
)
set /a array.index+=1
set %array.name%[%array.index%]=%array.value%
goto :eof


:len
REM Gets array length.
REM Arguments: (
REM name As "Array name"
REM var As "Output Variable"
REM )
set array.name=%1
set array.var=%2
for /f "delims=[=] tokens=2" %%a in ('set %array.name%[') do (
set %array.var%=%%a
)
goto :eof


:getitem
REM Get value of index in array.
REM Arguments: (
REM name As "Array Name",
REM index As "Item Index",
REM var As "Output Variable"
REM )
set array.name=%1
set array.index=%2
set array.var=%3
for /f "delims=[=] tokens=1,2,3" %%a in ('set %array.name%[') do (
if %%b==%array.index% set %array.var%=%%c
)
goto :eof

Let's see some application of this:

@echo off
set book[0]=Avatar
set book[1]=The Green Mile
set book[2]=The Count of Monte Cristo
call array.bat add book Babo
call array.bat len book length
echo I'm have %length% books you can borrow.
echo Pick one:
echo.
echo 0) Avatar
echo 1) The Green Mile
echo 2) The Count of Monte Cristo
echo 3) Babo
echo.
set /p pick=
call array.bat getitem book %pick% title
echo.
echo You picked %title%.
pause >nul
exit

11 thoughts on “Arrays in Batch

  1. Pingback: K11 Variable Voltage Power Supply Kit | myefficientplanet.com

  2. Pingback: Populating Array in DOS Batch Script Drija

  3. Either I am using this wrong, or it is flawed.

    1) Is there a maximum of 9 items?
    2) Needs a remove function to remove items by index and or value.
    3) Needs a clear function to purge array… this is especially important when calling array.bat multiple times from the same command prompt window.
    4) Add function doesn’t work well with values with spaces.

  4. I spent half o evening trying to find solution for lack of array in bat as well as lack of subvariables or multivariable (variable in variable).
    But i managed to make it a tricky way!

    I had text file with 18 lines and 4 columns separated by space,
    like this
    A B C D
    ….
    A B C D
    i needed read each line as set of 4 commands and use them as %1 %2 %3 %4 during function call, but that aint worked, so i decided to make several loops in loops to spawn a lot of indexed variables based on this text file.
    With this script i made, i have 72 indexed array vars from str_1_1 up to str_4_18

    for /f “tokens=* delims=” %%A in (sometextfile_for_search_replace_parsing.txt) do (
    set “String_0_=%%A”
    for /f “tokens=1,2,3,4,5,6 delims=,\” %%A in (“!String_0_!”) do (
    set “String_1_=%%A”
    set “String_2_=%%B”
    set “String_3_=%%C”
    set “String_4_=%%D”
    set “String_5_=%%E”
    set “String_6_=%%F”
    for /f “tokens=1 delims= ” %%A in (“!String_2_!”) do (
    set “String_2_=%%A”
    )
    for /f “tokens=1,2 delims=_” %%A in (“!String_5_!”) do (
    set “String_5A=%%A”
    set “String_5B=%%B”
    )
    for /f “tokens=1 delims= ” %%A in (“!String_6_!”) do (
    set “String_6_=%%A”
    )
    set “Number=0″
    for /f “tokens=* Delims=” %%a in (textfile_for_array_with_search_replace_pattern.txt) do (
    set “CmdValue=%%a”
    set /a “Number=!Number!+1″
    set “CmdNum_!Number!_=!CmdValue!”
    for /f “tokens=2,3,4,5 delims== ” %%A in (‘set CmdNum_!Number!_’) do ( ;this is where most tricky part begins, you cannot combine 2 vars by default, but you can use it in name of set variable, thats why i used set, to let variable expand as index number, than use tokens and delims to remove variable name and = symbol from output and spit rest of array into column chunks
    set “str1_!Number!_=%%A”
    set “str2_!Number!_=%%B”
    set “str3_!Number!_=%%C”
    set “str4_!Number!_=%%D”
    for /f “tokens=2 delims== ” %%A in (‘set str1_!Number!_’) do ( ; thats where second trick used, same as above but now im just converting indexed array into temporary fixed string, which valid just for this cycle
    set “str_1_=%%A”
    )
    for /f “tokens=2 delims== ” %%A in (‘set str2_!Number!_’) do (
    set “str_2_=%%A”
    )
    for /f “tokens=2 delims== ” %%A in (‘set str3_!Number!_’) do (
    set “str_3_=%%A”
    )
    for /f “tokens=2 delims== ” %%A in (‘set str4_!Number!_’) do (
    set “str_4_=%%A”
    )
    if “!String_6_!”==”!str_1_!” (
    set “String_6_=!str_2_!”
    if “!String_5A!”==”!str_3_!” set “String_5_=!str_4_!_!String_5B!”
    ) ; this is where main function works, im finaly able to compare string from input file with string from template array and replace if conditions match
    set “str1_!Number!_=”
    set “str2_!Number!_=”
    set “str3_!Number!_=”
    set “str4_!Number!_=”
    set “str_1_=”
    set “str_2_=”
    set “str_3_=”
    set “str_4_=”
    ;this is very important, due of set trick for indexind and due of fact that if you have few variables which starts with same digits or letters, set may output few of them and provoke extra loops with unxpected results, to avoid that name of variable should have some symbol after index number, and in the end of most inner for cycle all local indexed variables should be cleared to avoid parsing of next line output of set.
    )
    )
    echo !String_1_!, !String_2_!\!String_3_!\!String_4_!\!String_5_!\!String_6_!>>test.ini
    )
    set /a “Parsed=!Parsed!+1″
    cls
    echo Progress: !Parsed! of 150 lines parsed
    )

    And one more important thing – due of big number of variables and For cycles, script work pretty slow, about 30-60 seconds to parse old file and generate new one with changes.
    Also instead of of reading array from file like this
    for /f “tokens=* Delims=” %%a in (text_file_for_array.txt) do ( )
    you can add the real array as one line like this
    for /f “tokens=* Delims=” %%a in (“a b c d” “a b c d” “a b c d” “a b c d”) do ( ) where every quoted number of strings would act as line from text file and would be %%A in every cycle

  5. @echo off

    set array.func=%1
    set array.func.args=%2 %3 %4 %5 %6 %7 %8 %9
    call :%array.func% %array.func.args%
    goto :eof

    :add
    REM Adds a new item at the end of an array
    REM Arguments: (
    REM name As “Array Name”,
    REM value As “New value”
    REM )
    set array.name=%1
    set array.value=%2
    set %array.name%[ >nul 2>&1
    if %errorlevel% equ 0 for /f "delims=[=] tokens=1,2,3″ %%a in (‘set %array.name%[') do (
    set array.index=%%b
    )
    set /a array.index+=1
    set %array.name%[%array.index%]=%array.value%
    goto :eof

    :len
    REM Gets array length.
    REM Arguments: (
    REM name As “Array name”
    REM var As “Output Variable”
    REM )
    set array.name=%1
    set array.var=%2
    for /f “delims=[=] tokens=2″ %%a in (‘set %array.name%[') do (
    set %array.var%=%%a
    )
    goto :eof

    :getitem
    REM Get value of index in array.
    REM Arguments: (
    REM name As "Array Name",
    REM index As "Item Index",
    REM var As "Output Variable"
    REM )
    set array.name=%1
    set array.index=%2
    set array.var=%3
    for /f "delims=[=] tokens=1,2,3″ %%a in (‘set %array.name%[') do (
    if %%b==%array.index% set %array.var%=%%c
    )
    goto :eof

    :clear
    REM Clears array.
    REM Arguments: (
    REM name As "Array Name"
    REM )
    set array.name=%1
    call:len %array.name% length
    for /l %%a in (1,1,%length%) do set %array.name%[%%a]= 2>nul
    goto :eof

  6. Thanks for the help with the arrays. I’m new to batch the syntax is confusing to other languages. However your background is awesome, haha. Is it just one picture that changes it color or is it done programically?

  7. For anyone who is still interested, This code is broken for any array that requires more than 10 inputs. It takes a total of 10, anything that is added on after 10 replaces the 10th item. This is due to the way windows traverses through ‘set %array.name%[‘. It’s not in numerical order. e.g. 1,10,11,2,3,4,5,6,7,8,9 – sorting through 1-11.
    So in both OP’s code and @Audi Hsu code, they both are broken.
    This can be fixed by checking the index to see if it is numerically greater than the one that is currently stored, i.e. under the :add function > under the for loop, instead of set array.index=%%b replace with call %%b, where is another call function which does the comparison, e.g.

    :subFunction

    if %1 GTR %array.index% SET array.index=%1

    goto :eof

    I suppose you could avoid the subfunction using setlocal enabledelayedexpansion, but this works better.

    This should also be applied to the len function

    For those who are looking for a delete at index function, just do it in c. populating data with batch is already too slow, deleteing at index will definately take much longer. This method must push all items back by 1 cell, and it requires to traverse through the entire length NxN times to do it.

  8. Great code – thanks for that. One minor alteration I’d suggest to the sample usage. Add “pushd %~dp0″ (without quotes) just after “@echo off”. This will allow the script to be run from a UNC path, assigning it a drive letter / avoiding the security check (i.e. when “HKEY_CURRENT_USER \Software \Microsoft \Command Processor\DisableUNCCheck” is 0).

  9. I used arrays to make a “maze” game in batch:
    (adapted from a code example I found online)


    @echo off
    setlocal enabledelayedexpansion

    set arrayline[0]=#############
    set arrayline[1]=#...........#
    set arrayline[2]=#...........#
    set arrayline[3]=#..##...##..#
    set arrayline[4]=#..##...##..#
    set arrayline[5]=#...........#
    set arrayline[6]=#...........#
    set arrayline[7]=#...........#
    set arrayline[8]=#..##...##..#
    set arrayline[9]=#..##..,##..#
    set arrayline[10]=#...........#
    set arrayline[11]=#...........#
    set arrayline[12]=#############

    set pos_x=6
    set pos_y=6

    :game
    call :display
    call :move
    goto game

    :display
    set /a xAdd1=pos_x+1
    ::read it using a FOR /L statement
    for /l %%n in (0,1,12) do (
    set line=!arrayline[%%n]!
    if !line! equ %pos_y% (
    set line=!line:~0,%pos_x%!@!line:~%xAdd1%!
    )
    echo !line!
    set checkline[%%n]=!line!
    )
    goto :eof

    :move
    choice /n /c:wasd >nul
    set label=%errorlevel%
    set errorlevel=
    goto %label%
    if %errorlevel% == 1 goto move

    :1
    set /a pos_y-=1
    goto check4wall

    :2
    set /a pos_x-=1
    goto check4wall

    :3
    set /a pos_y+=1
    goto check4wall

    :4
    set /a pos_x+=1
    goto check4wall

    :check4wall
    set /a chars=%pos_x%+1
    set lineToCheck=!checkingline[%pos_y%]:~0,%chars%!
    set moveToTile=!lineToCheck:~-1!
    if !moveToTile! == # (
    set pos_x=!prev_x!
    set pos_y=!prev_y!
    )
    goto :eof

    • After changing/adding some stuffs, whole maze game is working. I’m sure these missing/wrong stuffs were not accident ;-)
      Cheers

      @echo off
      setlocal enabledelayedexpansion

      set arrayline[0]=#############
      set arrayline[1]=#………..#
      set arrayline[2]=#………..#
      set arrayline[3]=#..##…##..#
      set arrayline[4]=#..##…##..#
      set arrayline[5]=#………..#
      set arrayline[6]=#………..#
      set arrayline[7]=#………..#
      set arrayline[8]=#..##…##..#
      set arrayline[9]=#..##..,##..#
      set arrayline[10]=#………..#
      set arrayline[11]=#………..#
      set arrayline[12]=#############

      set pos_x=6
      set pos_y=6

      :game
      call :display
      call :move
      goto game

      :display
      cls
      set /a xAdd1=pos_x+1
      ::read it using a FOR /L statement
      for /l %%n in (0,1,12) do (
      set line=!arrayline[%%n]!

      if %%n equ %pos_y% (
      set line=!line:~0,%pos_x%!@!line:~%xAdd1%!
      )
      echo !line!
      set checkline[%%n]=!line!
      )
      goto :eof

      :move
      choice /n /c:wasd >nul
      set label=%errorlevel%
      set errorlevel=
      set prev_x=!pos_x!
      set prev_y=!pos_y!
      goto %label%
      if %errorlevel% == 1 goto move

      :1
      set /a pos_y-=1
      goto check4wall

      :2
      set /a pos_x-=1
      goto check4wall

      :3
      set /a pos_y+=1
      goto check4wall

      :4
      set /a pos_x+=1
      goto check4wall

      :check4wall
      set /a chars=%pos_x%+1
      set lineToCheck=!checkline[%pos_y%]:~0,%chars%!
      set moveToTile=!lineToCheck:~-1!
      if “!moveToTile!” == “#” (
      set pos_x=!prev_x!
      set pos_y=!prev_y!
      )
      goto :eof

  10. Hey, I must say, this is a very neat program you have created.

    I am using it in a Text-Based Role Play Game I am currently creating.

    I also added a “Subtraction” script.

    I am pretty sure you know how to make that, but, if you need the script email me!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: