Jim2B
Jim2B

Reputation: 177

Passing variable out of setlocal code

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:

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?

Upvotes: 6

Views: 2868

Answers (1)

jeb
jeb

Reputation: 82247

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

Upvotes: 5

Related Questions