17

I have a batch file which initializes variables via SET inside a for loop, for a set of files on disk:

for %%f in (%MYTARGETDIR%\*config.xml) do (
  SET TMPFILE=%%F.tmp

  echo In loop %TMPFILE%
)

echo End loop %TMPFILE%

when I run this in a brand new command shell (without TMPFILE defined) the In loop echo is empty, but the end loop echo is not.

When I run a second time, its already set, and outputs its value, ignoring the set until the loop closes.

Ideas why this is, and what the workaround is?

theschmitzer
  • 12,190
  • 11
  • 41
  • 49

5 Answers5

21

For the record, the corrected script looks like this:

setlocal ENABLEDELAYEDEXPANSION

for %%f in (%MYTARGETDIR%\*config.xml) do (

  SET TMPFILE=%%F.tmp

  echo In loop !TMPFILE!
)

echo End loop %TMPFILE%

Thanks chris for your help.

tmfahall
  • 172
  • 1
  • 13
theschmitzer
  • 12,190
  • 11
  • 41
  • 49
17

It because environment variables are substituted when the command is read. For the following command:

for %%f in (%mytargetdir%\*config.xml) do (
    set tmpfile=%%f.tmp
    echo In loop %tmpfile%
)

the entire command (from for to the closing parenthesis) is read and substituted before execution.

You need to use delayed expansion, such as with:

@echo off
    setlocal enableextensions enabledelayedexpansion
    set full=/u01/users/pax
:loop1
    if not "!full:~-1!" == "/" (
        set full2=!full:~-1!!full2!
        set full=!full:~,-1!
        goto :loop1
    )
    echo !full!
    endlocal

When you enable delayed expansion, the "%" markers still act as before but you can use the "!" markers to do the delayed expansion.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
9

That's because the ( ) block is parsed only once, it substitutes %TMPFILE% before it runs your loop.

laktak
  • 57,064
  • 17
  • 134
  • 164
6

You can also use CALL SET instead of delayed expansion.

George V. Reilly
  • 15,885
  • 7
  • 43
  • 38
2

Normally, I invoke CMD.EXE with the /e:on and /v:on switches to enable command extensions and delayed variable expansion. Then any batch scripts that I use are coded to check that delayed variable expansion is turned on.

This is the first time I have heard of SETLOCAL taking arguments. I checked with SETLOCAL /? and sure enough! This is such a time and code saver for me.

However, I did notice that command extensions must be enabled in CMD.EXE before SETLOCAL will recognize the ENABLEDELAYEDEXPANSION option. This is from the output of SETLOCAL /?

If Command Extensions are enabled SETLOCAL changes as follows:

SETLOCAL batch command now accepts optional arguments:
        ENABLEEXTENSIONS / DISABLEEXTENSIONS
            enable or disable command processor extensions. These
            arguments takes precedence over the CMD /E:ON or /E:OFF
            switches. See CMD /? for details.
        ENABLEDELAYEDEXPANSION / DISABLEDELAYEDEXPANSION
            enable or disable delayed environment variable
            expansion. These arguments takes precedence over the CMD
            /V:ON or /V:OFF switches. See CMD /? for details.

Thank you both for this valuable answer.