0

I am still struggling to properly understand the behaviour of Disable/EnableDelayedExpansion...

I want to parse input arguments when calling something like command -a -b -c file such to finally have options=-a -b -c and filename=file.

To do so I use the FOR /f loop:

set "count=0"
set "opts="
set "fl="
set tmpv=

:argloop
for /f tokens^=1^,^*^ delims^= %%a in ("%1") do (

    echo.
    echo Chosen option is %1
    set /a count+=1
    echo.
    echo Reading %count% is %%a..

    set "tmpv=%%a"
    rem setlocal enabledelayedexpansion
    echo Tmp is %tmpv% after set equal %%variable.
    rem endlocal
    rem setlocal disabledelayedexpansion
    set "tmpv=%tmpv:-=%"
    rem setlocal enabledelayedexpansion
    echo After removing it writes !tmpv!
    rem endlocal
    rem setlocal disabledelayedexpansion

    if "%tmpv%"=="%%a" (
        echo Input does not contain "-"
        set "fl=%tmpv%"
        echo %fl%

    ) else (

        echo/Options before are %opts%
        echo.
        if "%opts%"=="" (
            echo Options are empty.
            set opts=%%a
        ) else (
            set "opts=%opts% %%a"
        )
    )
    if not "%2"=="" (shift & goto:argloop)
)

echo.
echo Finally options are %opts%
set opts=%opts:-=/%
echo Finally options are %opts%
echo File name %fl%
set tmpv=
set count=
goto:end

Output writes:

Chosen option is -a

Reading 1 is -a..
Tmp is  after set equal %variable.
After removing it writes
Options before are

Options are empty.

Chosen option is -b

Reading 2 is -b..
Tmp is -= after set equal %variable.
After removing it writes -=
Options before are -a


Chosen option is -c

Reading 3 is -c..
Tmp is = after set equal %variable.
After removing it writes =
Options before are -a -b


Chosen option is flfl

Reading 4 is flfl..
Tmp is = after set equal %variable.
After removing it writes =
Options before are -a -b -c


Finally options are -a -b -c flfl
Finally options are /a /b /c flfl
File name

I had made it working with EnableDelayedExpansion, but not capable of storing final %fl% variable.

But why does it not work this way (without using delayed expansions)??

I will sincerely appreciate whom will try to clarify it in all extents.

mEm
  • 353
  • 3
  • 12

1 Answers1

0

The rules really aren't too hard.

You are aware that %var% is resolved to the value of var.

When a loop is parsed, every %var% within that loop is replaced by the THEN-current value of var. This includes pseudovariables like %cd%, %errorlevel% and %random%.

If delayedexpansion is in effect (and it is "in effect" from the setlocal enabledelayedexpansion instruction [start-of-setlocal-bracket] until an endlocal or end-of-file [end-of-setlocal-bracket] is reached) then !var! is resolved to the contents of var at the time that particular instruction (set, echo, etc) is executed.

If delayedexpansion is NOT in effect then !var! is simply that - the literal string !var!.

And one small kink. Any change made to the environment (addition, deletion or variation of variable values) is discarded when a setlocal bracket ends.

So, in all probability, you could display the difference by echoing %var% alongside !var!

  • and %%x (a metavariable) is always resolved to its current value, regardless of setlocal status.

[After responses]

Since all setlocal enabledelayedexpansion/endlocal brackets are remmed-out in the published code, I'm not surprised at the results.

However, running the published code does not yield the published results - for me, the response was "Reading 0"..."Reading 3".

So, looking at the for loop, I believe it's equivalent to

for /f "tokens=1,* delims=" %%a in ("%1") do (

which in turn is the same as

for /f "delims=" %%a in ("%1") do (

since there are no delimiters, an this is effectively the same as

for %%a in (%1) do (

which does nothing beyond assigning %1 to %%a and making the entire loop one block statement.

So therefore, this code should do the same job - without the kinkiness afforded by setlocal...

@ECHO OFF
SETLOCAL

set "count=0"
set "opts="
set "fl="
set "tmpv="

:argloopn
SET "arg=%1"
IF NOT DEFINED arg GOTO report
SET /a count+=1
ECHO arg %count% is %1 IN variable ARG = "%arg%"
SET "tmpv=%arg%"
rem remove "-"
set "tmpv=%tmpv:-=%"
IF "%tmpv%"=="%arg%" (
    echo Input does not contain "-"
    set "fl=%tmpv%"
) ELSE (
    ECHO Input contains "-" so is option
    SET "opts=%opts% %arg%"
)
SHIFT
GOTO argloopn

:report

rem fix-up options since 1st char, if it exists must be a space as [space]newoption is added each time
IF DEFINED options SET "options=%options:~1%"
echo Finally options are %opts%
set opts=%opts:-=/%
echo Finally options are %opts%
echo File name %fl%


echo.
GOTO :EOF

I've assumed that a proper batch-debug environment has been established; hence goto :eof to terminate the batch and an inintial setlocal to preserve the original environment.

When you use the point-click-and-giggle method of executing a batch, the batch window will often close if a syntax-error is found. You should instead open a 'command prompt' and run your batch from there so that the window remains open and any error message will be displayed.

--- BUT ---

In testing, I tried this:

@ECHO OFF
:parasite
setlocal
set "opts="
:loopp
FOR /f "delims=" %%a IN ("%1") DO (
 SET "opts=%opts% %%a"
 SETLOCAL ENABLEDELAYEDEXPANSION
  ECHO OPTS was "%opts%" is now "!opts!"
 ENDLOCAL
 ECHO %%opts%% is "%opts%" and !opts! is "!opts!"
)
SHIFT
IF "%1"=="" GOTO :EOF 
GOTO loopp

Which didn't do what I expected it to do - that is, report

 %opts% is " -a -b -c file" and !opts! is "!opts!"

Instead, it reported

 %opts% is " -a -b -c file" and  -a -b -c file is " -a -b -c file"

Which I find puzzling as the !var! is outside the setlocal enabledelayedexpansion/endlocal command-bracket and hence should not have been replaced, in my view.

Seems like an @jeb problem to me... so I'll see whether he's got an explanation...

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • I see, but precisely because it should not be so hard, I really don't understand what's going wrong in there. Both %tmpv% and !tmpv! are not behaving as supposed.. you see it? I refer to lines `echo Tmp is %tmpv% after set equal %%variable` and `echo After removing it writes !tmpv!`. In this latter it is evident how !tmpv! is not read literally.. Lastly, since I am not using any `setlocal enabledelayedexpansion` everything should be handled with `%tmpv%`, isn't it? – mEm Dec 28 '20 at 08:48
  • The problem is, you are changing variables within a code block (your `for` loop), so you need delayed expansion to use those changed variables. Please read [this](https://stackoverflow.com/questions/30282784/variables-are-not-behaving-as-expected/30284028#30284028) (several times, if needed; it's the key to understand your problem) – Stephan Dec 28 '20 at 09:23
  • @jeb : Care to comment on my last observation here? Too much yuletide tucker, perhaps? – Magoo Dec 28 '20 at 16:23