views:

39

answers:

3

I have a batch file that computes a variable via a series of intermediate variables:

@echo off
setlocal

set base=compute directory
set pkg=compute sub-directory
set scripts=%base%\%pkg%\Scripts

endlocal

%scripts%\activate.bat

The script on the last line isn't called, because it comes after endlocal, which clobbers the scripts environment variable, but it has to come after endlocal because its purpose is to set a bunch of other environment variables for use by the user.

How do I call a script who's purpose is to set permanent environment variables, but who's location is determined by a temporary environment variable?

I know I can create a temporary batch file before endlocal and call it after endlocal, which I will do if nothing else comes to light, but I would like to know if there is a less cringe-worthy solution.

+1  A: 

Something like the following (I haven't tested it):

@echo off 
setlocal 

set base=compute directory 
set pkg=compute sub-directory 
set scripts=%base%\%pkg%\Scripts 

pushd %scripts%

endlocal 

call .\activate.bat 
popd

Since the above doesn't work (see Marcelo's comment), I would probably do this as follows:

set uniquePrefix_base=compute directory 
set uniquePrefix_pkg=compute sub-directory 
set uniquePrefix_scripts=%uniquePrefix_base%\%uniquePrefix_pkg%\Scripts 
set uniquePrefix_base=
set uniquePrefix_pkg=

call %uniquePrefix_scripts%\activate.bat
set uniquePrefix_scripts=

where uniquePrefix_ is chosen to be "almost certainly" unique in your environment.

You could also test on entry to the bat file that the "uniquePrefix_..." environment variables are undefined on entry as expected - if not you can exit with an error.

I don't like copying the BAT to the TEMP directory as a general solution because of (a) the potential for a race condition with >1 caller, and (b) in the general case a BAT file might be accessing other files using a path relative to its location (e.g. %~dp0..\somedir\somefile.dat).

The following ugly solution will solve (b):

setlocal

set scripts=...whatever...
echo %scripts%>"%TEMP%\%~n0.dat"

endlocal

for /f "tokens=*" %%i in ('type "%TEMP%\%~n0.dat"') do call %%i\activate.bat
del "%TEMP%\%~n0.dat"
Joe
An inspired idea! I love it. Unfortunately, it doesn't work. I tried it, and found that endlocal also reverts to the working directory before setlocal was local.
Marcelo Cantos
See [Anonymous' answer](http://stackoverflow.com/questions/3262287/make-an-environment-variable-survive-endlocal/3262891#3262891) for a nice way of doing it. It gets messy with more variables but works fine without needing to create any files.
Joey
A: 

To answer my own question (in case no other answer comes to light, and to avoid repeats of the one I already know about)...

Create a temporary batch file before calling endlocal that contains the command to call the target batch file, then call and delete it after endlocal:

echo %scripts%\activate.bat > %TEMP%\activate.bat

endlocal

call %TEMP%\activate.bat
del %TEMP%\activate.bat

This is so ugly, I want to hang my head in shame. Better answers are most welcome.

Marcelo Cantos
+3  A: 

@ECHO OFF
SETLOCAL

REM Keep in mind that BAR in the next statement could be anything, including %1, etc.
SET FOO=BAR

ENDLOCAL && SET FOO=%FOO%

mistachkin
+1. That's the one. It works because environment variables are expanded while a line is parsed which means that once this line is run, first the `endlocal` will be executed and the variable in the `set` afterwards has already been expanded.
Joey