5

When writing scripts in Windows batch files, sometimes the proper execution of the script requires the use of the setlocal command. My main complaint about using setlocal is that I'm often performing complicated for & if statements in which I set variable values in that section of code. These settings are lost when I issue the command endlocal.

So far I've worked around this by echoing the variable value into a scratch file within the setlocal segment and then read the value back into the variable after the endlocal. However, it seems like there should be a more elegant solution to the problem.

The suggested answer provides a handy way to circumvent the issue if only using one or multiple set statements. However, the linked answer does not provide a solution when the setlocal is in place to allow a for loop to properly expand variable names at time of execution rather than parsing. In my situation, I also have if statement logic tree to perform further checking on that information to set many different possible variable values. The linked solution does not provide a solution to this situation.

This code is supposed to check an install package for its version number. It is called from another script that requires that version number to function correctly. We know the package is named using the form [application][version number].msi. The [version number] can be any of the following:

  • 7
  • 7.5
  • 9
  • 9.5
  • 10
  • 10.1

It is possible that more than one install package exists in the designated directory so it's important to look through them all and select the highest version in the directory.

I inherited and expanded the code to correctly process the 10 & 10.1 versions. If there's a better way to do this without the setlocal (e.g. I've thought of rewriting to use a case statement) I'd love to see a better solution.

However, I'm still interested in learning how to pass variables out of the setlocal / endlocal segment.

setlocal enabledelayedexpansion

for /f %%a in ('dir /b program*.MSI') do (
    set FileName=%%a
    set tst1=!FileName:~4,3!
    if "!tst1!" == "msi" (
        rem Only true if it has a 1 digit number (e.g. "9")
        set MAIN_VERSION=!FileName:~2,1!
    ) else (
        set tst2=!FileName:~5,3!
        if "!tst2!" == "msi" (
            rem Only true if it has a 2 digit version number (e.g. "10")
            set MAIN_VERSION=!FileName:~2,2!
        ) else (
            ... lots more code ...
        )
    )
)
rem Write results out to a file for temporary storage.  This particular
rem form of echo is required to ensure there are no trailing spaces, CR,
rem or LF
echo|set /P ="!MAIN_VERSION!" > %USERPROFILE%\UGV.txt

rem End local variables 
endlocal

rem Read the correct version out of our temporary storage file
set /p MAIN_VERSION=<%USERPROFILE%\UGV.txt

How do I pass a variable (such as MAIN_VERSION above) out of a Windows batch script setlocal / endlocal code segment without using a scratch file?

Jim2B
  • 167
  • 9
  • Possibly true. I'm working on using that feedback to see if it gets me what I want. I'll update my question if it appears the linked answer doesn't work. – Jim2B Nov 24 '15 at 16:12
  • The linked answer does not provide a solution for my question. The linked answer allows for single or multiple `set` statements to pass variable values back. In my situation, I need the set local so (for example) a `for` loop properly expands variable names at time of execution rather than parsing. However, within that `for` loop I also have an `if` statement logic tree that results in different possible variable values. The linked solution does not work for this situation – Jim2B Nov 24 '15 at 16:17
  • 3
    You should show a small code sample, that would better show why [In cmd.exe, how can you get one variable to escape the setlocal command?](http://stackoverflow.com/questions/3852563/in-cmd-exe-how-can-you-get-one-variable-to-escape-the-setlocal-command?lq=1) doesn't fit for you – jeb Nov 24 '15 at 16:24
  • 2
    You could also read [preserving exclamation marks in variable between setlocals batch](http://stackoverflow.com/q/29869394/463115) – jeb Nov 24 '15 at 16:31
  • @jeb, if I understood your solution correctly, the following code should work in place of my clunky job `endlocal & set MAIN_VERSION=!MAIN_VERSION!`? – Jim2B Nov 24 '15 at 16:41
  • 2
    Perhaps is this what you are looking for? `for %%v in (!MAIN_VERSION!) do endlocal & set MAIN_VERSION=%%v` – Aacini Nov 24 '15 at 16:48
  • @Jim2B: No you can't use delayed expansion for preserve values over the endlocal barrier (scope), but Aacini's solution should work for you. – jeb Nov 24 '15 at 16:51
  • @jeb: "preserve values over the endlocal barrier **(scope)**" **`:)`** – Aacini Nov 24 '15 at 16:53
  • @Aacini: Yes, **scope** was addressed to you – jeb Nov 24 '15 at 16:56

1 Answers1

5

To preserve variables over the setlocal/endlocal scope, there exists different solutions.

It depends of the situation which one you should use.

1) Simple

Works only outside of parenthesis blocks with simple content, but can produce problems with special characters like !^".

setlocal 
set localVar=something simple
...
(
  endlocal
  set "out=%localVar%"
)
set out

2) Medium

Works also in blocks and can handle the most characters, but can fail with !^ and linefeed/carriage return

if 1==1 (
  setlocal EnableDelayedExpansion
  set "localVar=something medium nasty & "^&"

  for /F "delims=" %%V in ("!localVar!") DO (    
      endlocal
      set "out=%%V"
  )
)
set out

3) Advanced

Works for all contents in any situation
SO: preserving exclamation marks in variable between setlocals batch

Community
  • 1
  • 1
jeb
  • 78,592
  • 17
  • 171
  • 225