2

I want to create an automated testing array in Windows Command using a single code statement as the VALUE for each testing array record. Here is what the array definition looks like for the first two records in the $code_test[xx] array:

set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)

I want to execute the VALUE of each $code_test[xx] array record (which is a test code statement) using a for /l loop like this:

for /l %%g in (0,1,1) do (
    echo $code_test[0%%g]
    !$code_test[0%%g]!
    echo.
)

The complete code is:

@echo off
setlocal enabledelayedexpansion

set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)

for /l %%g in (0,1,1) do (
    echo $code_test[0%%g]
    !$code_test[0%%g]!
    echo.
)

echo.
pause

When I execute the complete code I get the following error message when the !$code_test[0%%g]! line of code is executed:

'if' is not recognized as an internal or external command, operable program or batch file

I've read the excellent article here about how the IF command is parsed, but I don't see anything that jumps out at me as to why my code is failing. Is it even possible to do what I'm trying to accomplish?

Any Help Is Appreciated!

UPDATE: I discovered that @sst is correct when I used the set command in the set $code_test[01]=if 5 lss 10 (set /a $var1+=1) line of code:

@echo off

setlocal enabledelayedexpansion

set $var1=0
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (set /a $var1+=1)

for /l %%g in (0,1,1) do (
    echo $code_test[0%%g]
    cmd /c !$code_test[0%%g]!
    echo $var1 = !$var1!
    echo.
)

echo.
pause

The set /a $var1+=1 command didn't increment the value of $var1 as expected. In fact, it places a "1" in front of the $var1 label when the line of code echo $var1 = !$var1! executes. I haven't tried the solution offered by @Magoo yet. I'd really like to use the cmd /c solution instead of calling a subroutine. Is there anything I can do to make the set command work with the cmd /c option?

Bill Vallance
  • 471
  • 3
  • 19

3 Answers3

4

It can not work the way you tried, because the IF command is detected and handeled in phase 2.
But with delayed expansion it is too late.
This problem occurs only for the commands IF, FOR and REM.

´CALL %%$code_test%%` works in many cases, but fails also for the three commands and for code blocks.
It's unclear why it fails in that case, as the commands are parsed in phase 2 but they are not recognized.

The solution of @stephan uses a new cmd instance, it has the drawback, to lose all variable modifications every time the cmd instance finishes.

The solution of @Magoo uses therefore percent expansion, that should work in nearly all cases without problems.

jeb
  • 78,592
  • 17
  • 171
  • 225
3
@echo off
setlocal enabledelayedexpansion

set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)

for /l %%g in (0,1,1) do (
    SET "$code_test=!$code_test[0%%g]!
    CALL :test_code
    echo.
)

echo.
GOTO :EOF

:test_code
%$code_test%
GOTO :eof

Another approach.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • Thanks, @Magoo, for your answer. It works perfectly, even for the `set` command. For those that want a complete explanation about why this post was selected as the answer, please see the posts below. – Bill Vallance Jul 08 '19 at 16:13
2

you seem to need another layer of parsing:

@echo off
setlocal enabledelayedexpansion
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)
for /l %%g in (0,1,1) do (
    echo $code_test[0%%g]
    cmd /c !$code_test[0%%g]!
    echo.
)

Edit (just for completeness):
found another way to do it (without a subroutine):

@echo off
setlocal enabledelayedexpansion
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)
for /l %%g in (0,1,1) do (
    echo $code_test[0%%g]
    for /f "delims=" %%a in ('%%$code_test[0%%g]%%') do @set "result=%%a"
    echo !result!
    echo.
)

The trick is: for executes the ('...') part in a separate cmd process (as cmd /c ...) but captures the output into the main process.

Works both with !$code_test[0%%g]! and %%$code_test[0%%g]%%.

Although it doesn't work with if 5 lss 10 (set result=IT WORKED) because there is no output to capture (the set is only done with the do clause).

Stephan
  • 53,940
  • 10
  • 58
  • 91
  • This is exactly what I needed! Thanks @Stephan for helping me figure this out. – Bill Vallance Jul 07 '19 at 16:45
  • Just out of curiosity, @Stephan, why doesn't the statement "call %%!$code_test[0%%g]!%%" work (which I tried)? Why do I have to launch a new instance of cmd instead? – Bill Vallance Jul 07 '19 at 17:01
  • @BillVallance, Just be aware that while this works for your specific case it may not always work as you may expect. For instance `set $code_test[00]=if 5 lss 10 (set foo=bar)` will probably surprises you. Handing over the the commands to child cmd have its own consequences which may or mat not be desired, depending on the context. For that matter you may want to take a closer look at @Magoo's answer if you haven't so far. – sst Jul 07 '19 at 17:18
  • Thanks, @sst, for the information. You are absolutely correct, as I discovered when I tried to use the `set` command in my code. I've updated my question with new code. Anybody have an idea what I'm doing wrong? – Bill Vallance Jul 07 '19 at 18:14
  • The `set`command works, but when the cmd instance is closed (after each comman) the variables are lost. @Magoo's answer solves that. Why `CALL %%!$code...` doesn't work, is a bit unclear, see my anser – jeb Jul 08 '19 at 06:05
  • sst and jeb are completely right. `cmd /c` opens a new process; every change in that process is lost after the command finishes. It's fine to `echo` something (or to write to a file) like you asked on your original question, but setting variables is pointless, as they are immediately lost when the process ends. – Stephan Jul 08 '19 at 07:26
  • Thanks, @jeb and @Stephan, for your added explanations. I will change my answer to the one provided by @Magoo since I need to utilize the `set` command in my test cases. I apologize to @Stephan for not including a `set` command in my original post. – Bill Vallance Jul 08 '19 at 16:08