2

How can I execute code saved in a variable? I'm trying to execute an if statement that I have saved in another file (that must remain a text file). Clues on how to execute an if statement just from a variable might help, as I assume the problem is that it can't read the %%s.

Text file contains:

if %var%==0301 (echo Yay) 

Batch file contains:

for /f "tokens=*" %%s in (code.file) do (
%%s
)

This normally executes the code in code.file by setting everything in code.file to the variable %%s and then executing the variable.

The result is this: 'if' is not recognized as an internal or external command, operable program or batch file.

This method works for executing echo and set, but I need it to work for if.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Kindly exemplify – Magoo Aug 16 '22 at 19:34
  • You haven't really posted enough code for us to give meaningful advice, but I will say that `%%s` only exists for the duration of the `for` loop, so if you're trying to use the value after the very last `)`, you'll need to do something like `for /f "tokens=*" %%s in (code.file) do set "value=%%s"` and then use `%value%` instead. – SomethingDark Aug 16 '22 at 20:01

2 Answers2

2

The IF is detected by the parser in phase2 only, but %%s will be expanded in a later phase.

You need to use a percent expansion for it, like in this sample

for /f "tokens=*" %%s in (code.file) do (
  set "line=%%s"
  call :executer
)
exit /b

:executer
%line%
exit /b

But for your sample: if %var%==0301 (echo Yay)
It will never say Yay, because %var% will never be expanded.

This is because %line% is already a percent expansion, it will not work recursively.

That could be solved by changing the text in code.file to if !var! == 0301 (echo Yay)
This works, because the delayed expansion happens after the percent expansion

Or a much simpler solution:

copy code.file tmp.bat
call tmp.bat
del tmp.bat
jeb
  • 78,592
  • 17
  • 171
  • 225
1

The major problem at hand is that the command interpreter particularly handled the commands if, for and rem: These commands are recognized earlier than every other one, even before for meta-variables like %%s become expanded. Therefore, these commands are no longer detected after expansion of %%s.

Refer to: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
According to this, said commands are detected during Phase 2, while expansion of for meta-variables happens in Phase 4. Other commands are found later in Phase 7.

A possible way to work around that is to use a sub-routine, which %-expansion occurs in, which happens in Phase 1, hence before recognition of the three special commands:

for /f "tokens=*" %%s in (code.file) do (
    rem // Execute the read command in a sun-routine:
    call :SUB %%s
)
goto :EOF

:SUB
rem // Take the whole argument string as a command line:
%*
goto :EOF
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • 1
    Minor critics: Using `call :SUB %%s` and expansion of `%*` leads to new problems with string modifications by `call` and reduction by `%*` – jeb Aug 16 '22 at 21:13
  • Thanks! I never even knew there were different phases! – James Lewis Aug 16 '22 at 21:26
  • @jeb, true. A possibility was to replace `call :SUB %%s` by `set "ARGS=%%s" & call :SUB %%ARGS%%` to solve issues with `call`, although this would still not execute the command line `if %var%==0301` in the sub-routine as expected… – aschipfl Aug 16 '22 at 23:29
  • 1
    But it still fails with `echo one & echo two`, I suppose arguments should be avoided at all in the CALL – jeb Aug 17 '22 at 09:42
  • You can use 'call :SUB' without parameters if you use a dummy for-loop in :SUB This works because all for-parameters are global inside a for-loop. Use in :SUB only the following line: 'for %%. in (.) do %%s' – OJBakker Aug 17 '22 at 16:35
  • Sure, @OJBakker, but then you'll have the same problem like in the original post… – aschipfl Aug 17 '22 at 16:40