0

I have an issue with my batch script likely related to the nested blocks syntax.

I have two scripts: a main script, and a start script in order to start the main one. Both are respectively named "script.bat" and "start.bat" for the example.

Below the scripts (MCVE):

script.bat

@echo off
set init="%1"
set bool=%2
set switch=%3
if %bool%==true (
    set var=%init:"=%
    set check=false
    if "%switch%"=="I" (
        if "%var%"=="A" set check=true
        if "%var%"=="B" set check=true
        if "%var%"=="C" set check=true
        if "%var%"=="D" set check=true
        if "%var%"=="E" set check=true
        if "%var%"=="F" set check=true
        if "%var%"=="G" set check=true
    )
    if "%switch%"=="II" (
        if "%var%"=="A" set check=true
        if "%var%"=="B" set check=true
        if "%var%"=="C" set check=true
        if "%var%"=="D" set check=true
        if "%var%"=="E" set check=true
    )
    if "%switch%"=="III" (
        if "%var%"=="A" set check=true
        if "%var%"=="B" set check=true
        if "%var%"=="C" set check=true
        if "%var%"=="D" set check=true
        if "%var%"=="E" set check=true
        if "%var%"=="F" set check=true
    )
    if %check%==false set code=1
    if %check%==true set code=0
    if %check%==true set result=%var%
)
if %bool%==false echo Skipped!
if %bool%==false goto quit
if defined result (
    echo RESULT = %result% [exit code: %code%]
) else (
    echo RESULT = NULL [exit code: %code%]
)
:quit
echo.
pause
exit

start.bat

@echo off
start script.bat C true II
start script.bat F true II
start script.bat B false I

Here is the expected results (It should prompt three Windows consoles as below.)

First console:

RESULT = C [exit code: 0]

Press any key to continue...

Second console:

RESULT = NULL [exit code: 1]

Press any key to continue...

Third console:

Skipped!

Press any key to continue...

Instead of that, I have the error below in each of the three consoles:

set was unexpected at this time.

Thanks for your help and sorry if my english is inaccurate sometimes.

Have a nice day. :)

EDIT: this problem seems to be not related with the delayed expansions because we have a syntax error here. A "delayed expansion" related issue should lead to a wrong result instead but not a syntax error like in my example.

EDIT 2: after trying with delayed expansions, thats worked! Please, disregard the previous edit.

  • 2
    Possible duplicate of [Example of delayed expansion in batch file](https://stackoverflow.com/questions/10558316/example-of-delayed-expansion-in-batch-file). The variables `var` and `check` require delayed expansion here... For nesting `if` conditions see also [this post](https://stackoverflow.com/a/50066728)... – aschipfl May 07 '18 at 15:43
  • Ok, you solved my problem. I'm surprised by the need of delayed expansion to deal with the nested blocks. – Mikhail Lomaz May 07 '18 at 16:01
  • That's because the variables become expanded when the whole block is read and parsed in case normal `%`-expansion is used, but not when it's executed; that's what you can achive with delayed `!`-expansion... – aschipfl May 07 '18 at 16:04
  • There is any way to prevent the use of "setlocal"? Because this causes the loss of the variables. – Mikhail Lomaz May 07 '18 at 16:07
  • There is no (easy) way to avoid `setlocal`, but you can transport the variables beyond `endlocal` like this: `endlocal & set "var=%var%" & set ...`, or writing as a multi-line block, like this: `(` -- `endlocal` -- `set "var=%var%"` -- `set ...` -- `)` (where '--' represents a line-break here) – aschipfl May 07 '18 at 16:12
  • Ok thanks for your help. And I have a final question to conclude: is the setlocal/endlocal commands are resource demanding? Particulary in heavy scripts. – Mikhail Lomaz May 07 '18 at 16:18
  • Actually I can't provide an objective statement concerning that, but I don't think such blocks are consuming many resources; I've written several scripts where `setlocal`/`endlocal` blocks appear within `for` loops, and I they mostly run much faster than alternative ones that avoid those blocks (by using [`call`](http://ss64.com/nt/call.html#AdvancedusageCALLinginternalcommands) for in-block expansion, for instance)... – aschipfl May 07 '18 at 16:26
  • Ok, thank you for your replies, now it's clear. Have a nice day. – Mikhail Lomaz May 07 '18 at 16:29
  • @MikhailLomaz, because `start.bat` appears to be in the current working directory, if you name your script `start.bat` and inside it you use the command `start script.bat C true II`, it may mean that you're actually running the command, `start.bat script.bat C true II`. I don't think that is what you wanted, so I'd strongly suggest you choose a script name which is not also the name of an otherwise valid executable. Regardless of that, you should probably be using the `Call` command instead of the `Start` command anyway! – Compo May 08 '18 at 13:26

2 Answers2

0

As aschipfl said, it was a problem related to the delayed expansions.

Here is the working script.bat:

@echo off
set init="%1"
set bool=%2
set switch=%3
setlocal enabledelayedexpansion
if %bool%==true (
    set var=%init:"=%
    set check=false
    if "%switch%"=="I" (
        if "!var!"=="A" set check=true
        if "!var!"=="B" set check=true
        if "!var!"=="C" set check=true
        if "!var!"=="D" set check=true
        if "!var!"=="E" set check=true
        if "!var!"=="F" set check=true
        if "!var!"=="G" set check=true
    )
    if "%switch%"=="II" (
        if "!var!"=="A" set check=true
        if "!var!"=="B" set check=true
        if "!var!"=="C" set check=true
        if "!var!"=="D" set check=true
        if "!var!"=="E" set check=true
    )
    if "%switch%"=="III" (
        if "!var!"=="A" set check=true
        if "!var!"=="B" set check=true
        if "!var!"=="C" set check=true
        if "!var!"=="D" set check=true
        if "!var!"=="E" set check=true
        if "!var!"=="F" set check=true
    )
    if !check!==false set code=1
    if !check!==true set code=0
    if !check!==true set result=!var!
)
if %bool%==false echo Skipped!
if %bool%==false goto quit
if defined result (
    echo RESULT = %result% [exit code: %code%]
) else (
    echo RESULT = NULL [exit code: %code%]
)
:quit
endlocal
echo.
pause
exit
0

The big code block if %bool%==true ( isn't neccessary at all if you put
if %bool%==false (echo Skipped! & goto quit)
in front (assuming bool should either be true or false)
All your IF commands do miss the /I ignore casing switch.

I'd restructure the batch and avoid the neccessity for DelayedExpansion
You can use findstr to check entries for validity.

:: Q:\Test\2018\05\07\SO_50217905.cmd
@echo off
set "init=%~1"
set bool=%2

:: check bool for valid content
Echo:%bool%|findstr /i "true false" >Nul 2>&1 || (Echo wrong bool value&goto quit)

set switch=%3
Echo:%switch%|findstr /i "II*" >Nul 2>&1 || (Echo wrong switch value&goto quit)

if /i %bool%==false (echo Skipped! & goto quit )

set "var=%init:~0,1%"
set check=false

if "%switch%"=="I"   Echo:ABCDEFG|findstr /i "%var%" >NUL 2>&1 && set check=true
if "%switch%"=="II"  Echo:ABCDE|  findstr /i "%var%" >NUL 2>&1 && set check=true
if "%switch%"=="III" Echo:ABCDEF| findstr /i "%var%" >NUL 2>&1 && set check=true

if /i %check%==false set code=1
if /i %check%==true (set code=0 & set result=%var%)

if defined result (
    echo RESULT = %result% [exit code: %code%]
) else (
    echo RESULT = NULL [exit code: %code%]
)
:quit
echo.
pause
exit /B %code%