Close

Windows Batch Files: Delayed Evaluation

A project log for install-avr-tools

Given Windows with some version of Arduino installed, set the paths to allow using avr-gcc/etc from the command line.

westfwWestfW 02/23/2017 at 10:441 Comment

wiontinuing from previous log...

With "delayed evaluation", you replace your variable expansions "%A%" with "!A!", and they get expanded just as they are executed (when you'd expect!) But since this was a backward-incompatible enhancement to .bat files (at some time in the distant path, you have to actually turn this on explicitly for it to work.

The first way that I found to turn this on is to invoke cmd.exe with a "/V" switch. So if I have foo.bat with:

SET wholestring=AAAA
for %%s in (first second third forth) DO (
  SET wholestring=!wholestring! %%s
)
echo wholestring is %wholestring%
I can say "cmd /v /c foo.bat", and I'll get the expected "AAAA first second third forth" output. I didn't find a way to turn this on permanently, and it wouldn't be a good thing to ask of the target audience for this script. I thought I could be very clever and automatically detect whether it was in use or not:
if !SystemDrive! NEQ %SystemDrive% (
  cmd.exe /v /c "%0"
) else (
  SET wholestring=AAAA
  for %%s in (first second third forth) DO (
    SET wholestring=!wholestring! %%s
  )
  echo wholestring is %wholestring%
)
(if not running in Delayed Evaluation mode, run the same file with /V turned on. Otherwise do the code that counts on delayed evaluation...)

BUT - you recall our goal eventually is to set the user environment PATH variable. It turns out that when you invoke cmd.exe like this, any changes the file makes to the PATH variable disappear when that sub-execution of cmd.exe exits. :-(

This is a problem that is probably familiar to anyone who has written any unix shell scripts. The ENVIRONMENT variables and the shell variables are separate - if your copy of the shell (cmd.exe in this case) creates a variable, you have to do something special to get that value "up" into the environment of the parent. All I needed to do was find the cmd.exe equivalent for "export" or "setenv."

Except, all the windows batch file tutorials/etc that I could find didn't seem to think that there was any problem changing the path in a .bat file script, and so there doesn't seem to be any equivalent for modifying the parent/global variables. It made a certain amount of sense that running a .bat file, and running cmd.exe to run a .bat file might be different, so I went off on a search to find a way to end up setting the path from the original .bat file instead of the sub-process.

I had thought I had found it with "SETLOCAL EnableDelayedExpansion" command. This turns on delayed expansion, just like running cmd with the "/V" switch. However, the original "SETLOCAL" command (no arguments) has the effect of making all variables used thereafter be local instead of global, including the PATH. Presumably, running "cmd /v" works exactly like stuffing a "SETLOCAL EnableDelayedExpansion" command in your .bat file as the first command.

There's an ENDLOCAL command as well, that lets you start modifying global variables again. Perhaps I can do what I need with local variables, then turn them off and put the results into global variables? Nope - it seems that ENDLOCAL deletes any local variables that you had, even if they didn't overlap any global variables. I guess that makes sense, since a variable that merely exists can change behavior, and you wouldn't want that to happen accidentally. But grrr - how do you pass information from your "locally careful" code to something that has to operate globally??

Discussions

penknife wrote 07/11/2018 at 09:53 point

endlocal & set "globalVal=%localVal%"

Or

(

endlocal

 set "globalVal=%localVal%"

)

  Are you sure? yes | no