0

I want to initialize a matrix, and then iterate over it in several places.

This is what I have so far based on what I can find from other questions, but clearly I am doing something horribly wrong because SERVICES is null in the echo.

Code

rem SET SERVICES[#][0]="Window Title"
rem SET SERVICES[#][1]="Relative .bat path"

SET SERVICES[0][0]="Hello World!"
SET SERVICES[0][1]="service/hello.bat"
SET SERVICES[1][0]="Hello World! 2"
SET SERVICES[1][1]="service/hello 2.bat"

for %%s in (%SERVICES%) do call ServiceStarter.bat %%s[0] %%s[1]

for %%s in (%SERVICES%) do call ServiceStopper.bat %%s[0]

Output

C:\securboration>SET SERVICES[0][0]="Hello World!"

C:\securboration>SET SERVICES[0][1]="service/hello.bat"

C:\securboration>SET SERVICES[1][0]="Hello World! 2"

C:\securboration>SET SERVICES[1][1]="service/hello 2.bat"

C:\securboration>for %s in ((null)) do call ServiceStarter.bat %s[0] %s[1]

C:\securboration>for %s in ((null)) do call ServiceStopper.bat %s[0]

Expected

It should loop though the outer array so that I can pass the elements of the sub array as parameters to other .bat files.

So What is wrong with the Matrix initialization, and once setup, how do I iterate over that matrix to pass the values on to other calls?

This might have been answered before, but I think the other answers assume a higher level of knowledge than I have, so can you explain in a way that requires no working batch-file knowledge? Also, supporting the spaces is important.

Tezra
  • 8,463
  • 3
  • 31
  • 68
  • 2
    Take a look at this: [Arrays, linked lists and other data structures in cmd.exe (batch) script](https://stackoverflow.com/questions/10166386/arrays-linked-lists-and-other-data-structures-in-cmd-exe-batch-script/10167990#10167990) – aschipfl Jun 12 '17 at 20:28

2 Answers2

1
<!-- language: lang-dos -->

@ECHO OFF
SETLOCAL
rem SET SERVICES[#][0]="Window Title"
rem SET SERVICES[#][1]="Relative .bat path"

SET SERVICES[0][0]="Hello World!"
SET SERVICES[0][1]="service/hello.bat"
SET SERVICES[1][0]="Hello World! 2"
SET SERVICES[1][1]="service/hello 2.bat"

for /f "tokens=2,3*delims=[]=" %%a in ('set SERVICES[') do IF %%b==0 CALL call q44506472_s.bat %%services[%%a][0]%% %%services[%%a][1]%%
for /f "tokens=2,3*delims=[]=" %%a in ('set SERVICES[') do IF %%b==0 CALL call q44506472_s.bat %%c %%services[%%a][1]%%


GOTO :EOF

Where q44506472_s.bat is a dummy demo file containing

@ECHO OFF
SETLOCAL
ECHO parameter1=%1
ECHO parameter2=%2
ECHO ----------------

(just to show the parameters being transmitted)

So - the hieroglypics do this:

Perform a set services[ command, which lists all variables that start services[ in the format services[0][1]=somestring.

The for /f tokenises each line that the set produces, using the three characters [,] and = as delimiters. So, with services[0][1]=somestring, we get

services as token1 - unassigned
0 as token2 - assigned to %%a as 2 is the first token number nominated
1 as token3 - assigned to %%b as 3 is the next token number nominated
somestring as token* (remainder after highest-nominated token) - assigned to %%c

The tokens are separated by any sequence of any of the chosen delimiters.

So, choosing %%b==0 will select the second array dimension = 0, and we then execute the call by calling for example %%services[%%a][0]%% which is resolved as the contents of services[0][0]. Since %%a is an active metavariable at this time, it gets substituted first and the parser then resolves remaining %% as % (% escapes %).

The first call does the parsing party-trick; the second executes the actual subroutine with the parameters resolved.

Since %%c gratuitously contains the contents required for the first parameter, you could also use that if you prefer.


Your development from there - well, cmd isn't the brightest - I think you're getting a little too clever for it. There are two problems - the first is that the windowtitle... is quoted, causing havoc and the second is that you are "quoting strings" as variable-values. Personally, I apply quotes as necessary, and I use the cmd characteristic syntax SET "var=value" (where value may be empty) which is used to ensure that any stray trailing spaces are NOT included in the value assigned.

I'd change

 for /f "tokens=2,3*delims=[]=" %%a in ('set SERVICES[') do IF %%b==0 CALL for /f "Delims=:" %%A in ('tasklist /v /fi "WINDOWTITLE eq %%services[%%a][0]%%"') do if %%A==INFO SET errorinfo=1

to

(note that cmd is quite happy with line-breaks directly before/after the single-quotes in a for/f command - just makes the lines easier to edit...)

for /f "tokens=2,3*delims=[]=" %%a in ('set SERVICES['
 ) do IF %%b==0 CALL :seterrorinfo %%services[%%a][0]%%

ECHO errorinfo="%errorinfo%"

GOTO :EOF

:seterrorinfo
for /f "Delims=:" %%A in ('tasklist /v /fi "WINDOWTITLE eq %~1"') do ECHO ***%%A***&if %%A==INFO SET errorinfo=1

GOTO :eof

:seterrorinfo is now a subroutine, supplied with the quoted-value from the array as a parameter.

Within the subroutine, %~1 get substituted by the value of the first parameter, with the ~ operator removing any enclosing quotes.

I'll not comment on whether the detection of INFO is appropriate (but I'd suggest that errorinfo be cleared at the start of the routine. Consider what will happen if the routine is called more than once - the value from the previous call will remain in errorinfo if %%A is not INFO on the next call.) I've left the echo %%A in the subroutine for easy analysis.

Note that if you read some of the articles about delayed expansion on SO, you may be able to use it to avoid the call set kludge. Your original data included ! so I avoided it as ! then becomes a special character.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • Thanks a ton! I'm trying to use this now to confirm all the windows opened (didn't crash on start) using `for /f "tokens=2,3*delims=[]=" %%a in ('set SERVICES[') do IF %%b==0 CALL for /f "Delims=:" %%A in ('tasklist /v /fi "WINDOWTITLE eq %%services[%%a][0]%%"') do if %%A==INFO SET errorinfo=1`; but is says `IF 0 == 0 CALL for /f "Delims=:" %A in ('tasklist /v /fi "WINDOWTITLE eq %services[4][0]%"') do if %A==INFO SET errorinfo=1 services[4][0]A was unexpected at this time.` What did I do wrong here? – Tezra Jun 13 '17 at 14:38
  • Thank you so much for your black-magic expertise. ^^ The script works now. – Tezra Jun 16 '17 at 15:39
0

This can be done in a simpler fashion still, using the most basic of for loops. The key is in iterating over the correct part of the variable name to reduce code required to expand it.

In this instance, the available service indexes and their seperators.

@echo off
    Set Services="0][0" "0][1" "1][0" "1][1"
    Set SERVICES[0][0]="Hello World!"
    Set SERVICES[0][1]="service/hello.bat"
    Set SERVICES[1][0]="Hello World! 2"
    Set SERVICES[1][1]="service/hello 2.bat"

For %%A in (%Services%) do (
    CALL echo(%%Services[%%~A]%%
    Choice /N /C NY /M "Start Service? NY"
    If Errorlevel 2 Call :service %%Services[%%~A]%%
)
    pause
    Exit /B
:service
    Echo( Service Selected: %1
    pause
Exit /B

Additionally, you can assign each index to a particular command to action:

@echo off
    Set Services="0][0" "0][1" "1][0" "1][1"
    Set "0][0=Echo(A command or Parameter for service 0 0"
    Set "0][1=Echo(A command or Parameter for service 0 1"
    Set "1][0=Echo(A command or Parameter for service 1 0"
    Set "1][1=Echo(A command or Parameter for service 1 1"
    SET SERVICES[0][0]="Hello World!"
    SET SERVICES[0][1]="service/hello.bat"
    SET SERVICES[1][0]="Hello World! 2"
    SET SERVICES[1][1]="service/hello 2.bat"

    Setlocal EnableDelayedExpansion
For %%A in (%Services%) do (
    echo(!Services[%%~A]!
    Choice /N /C NY /M "Start Service? NY"
    If Errorlevel 2 Call :service "!%%~A!" "!Services[%%~A]!"
)
pause
Exit /B
:service
%~1 !%~2!
pause
Exit /B

T3RR0R
  • 2,747
  • 3
  • 10
  • 25