Rich Jahn
Rich Jahn

Reputation: 443

How do you set an environment variable in a subroutine when setlocal is enabled?

I am trying to use setlocal/endlocal to use some environment variables internally in a script, but then I do want the script to ultimately save some environment variables as output (i.e., that do take effect in the calling environment).

I thought I could simply have a setlocal at the beginning, and then wrap any set commands I want to do outside the script with an endlocal before and setlocal after. But it seems to have different behavior if done inside a subroutine vs. outside.

This script should have %var1% == test1 in the calling environment, but not have %var2% == test2.

@echo off

:main
setlocal
call :set_var1
set var2=test2
exit /b

:set_var1
endlocal
set var1=test1
setlocal
exit /b

However, when I run this, I see:

D:\>set var1=

D:\>echo %var1%
%var1%

D:\>set var2=

D:\>echo %var2%
%var2%

D:\>test.bat

D:\>echo %var1%
%var1%

D:\>echo %var2%
%var2%

So the script failed to set %var1%.

If I "inline" the code (i.e. cut and paste the code inside the subroutine in the same spot as the subroutine call, and delete the subroutine call), so the script should do the same thing, then it works:

@echo off

:main
setlocal
endlocal
set var1=test1
setlocal
set var2=test2
exit /b

The output looks like:

D:\>set var1=

D:\>echo %var1%
%var1%

D:\>set var2=

D:\>echo %var2%
%var2%

D:\>test.bat

D:\>echo %var1%
test1

D:\>echo %var2%
%var2%

So now %var1% was correctly set to test1 by the script.

I'm wondering if the exit /b that returns from a subroutine has some effect on setlocal/endlocal. I don't see anything in the /? usage about this.

How do you selectively have a few environment variables created, or modified, in the batch script, take effect in the calling environment, but in general have most of the variables used for intermediate steps, discarded (i.e., treat them as local to the script)?

Upvotes: 1

Views: 537

Answers (1)

Mofi
Mofi

Reputation: 49086

I suggest following:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

:main
call :set_var1
set "var2=test2"
rem Other commands which define other environment variables like:
set "var3=test3"
endlocal & set "var1=%var1%" & set "var3=%var3%"
exit /B

:set_var1
set "var1=test1"
goto :EOF

There is defined once completely the required execution environment at top of the batch file with the first two command lines. Read this answer for details about the commands SETLOCAL and ENDLOCAL.

The command line with endlocal is parsed by the Windows Command Processor cmd.exe as described by How does the Windows Command Interpreter (CMD.EXE) parse scripts? The environment variable references %var1% and %var3% are replaced by the current values of these two environment variables in local environment of the batch file.

The finally executed endlocal command line does not contain anymore environment variable references. The command line on execution is:

endlocal   & set "var1=test1"   & set "var3=test3"

The additional spaces are inserted by cmd.exe on parsing the command line.

There is first restored the initial environment on starting the processing of this batch file and then is defined the environment variable var1 with the string test1 and next var3 with the string test3 in that environment.

See also:

There is used goto :EOF to exit the subroutine set_var1 and exit /B to exit the processing of the batch file. The reasons are:

  1. The usage of exit /B for exiting batch file processing and goto :EOF for exiting processing of a subroutine makes it easier to see which processing is exited – a subroutine or the batch file processing. It makes it easier finding the exit points of the batch file.
  2. The command goto :EOF does not work with disabled command extensions. The second line of the batch file explicitly enables the command extensions. goto :EOF works for that reason always for the subroutine. But it could not work for exiting the batch file processing after the command line with endlocal on command extensions are disabled for whatever reason in initial environment on starting the processing of the batch file.
  3. The command exit /B always works even with disabled command extensions. It would be even better to use exit /B 2>nul to suppress the error message output because of missing label EOF on running exit /B in initial execution environment with disabled command extension. However, command extensions are enabled by default on nowadays used Windows versions. There is really no reason to disable them ever, except a batch file is written on Windows XP and newer Windows versions for usage on MS-DOS, Windows 3.1, Windows 3.11, Windows 95/98/ME with COMMAND.COM as command interpreter instead of the Windows Command Processor cmd.exe.

Upvotes: 1

Related Questions