5

I have been successfully using the CALL mechanism to allow one batch file to CALL another to setup environment variables. This code has been working well for over a year on Windows XP.

However, it does not appear to be working in the same way on Windows 7. The variables exist in the second batch file just before the EXIT /B statement. But, they do not exist upon the return to the first batch file.

Some trivial examples seem to work as expected, but the large batch scripts do not.

Has anyone had difficulties with this or know any workarounds?

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
lit
  • 14,456
  • 10
  • 65
  • 119
  • 3
    Be sure the second Batch file does NOT have a `setlocal` command. – Aacini Jun 03 '14 at 17:21
  • The second batch script does have several `SETLOCAL` commands. I am using `ENDLOCAL` to ensure that the local variables are set after the local scope ends. – lit Jun 03 '14 at 19:16
  • 1
    I need to be able to use `SETLOCAL` in the second batch file in order to use `ENABLEDELAYEDEXPANSION`. – lit Jun 03 '14 at 22:13
  • Sounds like trial-and-error debugging may be the only way to go here. Tear out sections of code until the problem goes away, then narrow in on the exact trigger(s). (In particular, find out whether removing all the calls to SETLOCAL makes the problem disappear.) – Harry Johnston Jun 05 '14 at 23:08
  • Also, try putting a `pause` at the end of the second batch file just before `exit /b` and then double-check that only one instance of `cmd.exe` is running. Oh, and you might try changing `exit /b` to `goto :eof` - long shot, but can't hurt. :-) – Harry Johnston Jun 05 '14 at 23:10
  • 1
    Possible duplicate of [Make an environment variable survive ENDLOCAL](https://stackoverflow.com/questions/3262287/make-an-environment-variable-survive-endlocal) – ivan_pozdeev Jun 03 '17 at 12:39

2 Answers2

9

In years of advanced batch scripting, I have never seen a CALL fail to preserve environment variables unless the called script (or label) set the variable when SETLOCAL was still active. There is an implicit ENDLOCAL for every active SETLOCAL from the within the CALL upon termination of the CALL.

It sounds like you have put in diagnostic messages prior to your EXIT /B to confirm that your variables are defined. I would take it one step further and add multiple ENDLOCAL statements prior to your diagnostic messages. I suspect you will then see your values dissapear prior to EXIT /B. You can add as many ENDLOCAL as you want. ENDLOCAL will never affect SETLOCAL that occurred prior to the CALL.

The most likely explanation is that either your script has somehow changed from XP to Win 7, or else there is some context change in your Win 7 environment that is exercising some aspect of the code that hadn't been exposed before.

dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Many thanks for picking up on this. Today I learned that it is working on another Windows 7 machine, just not mine. I have an explicit `ENDLOCAL` for every `SETLOCAL` statement. I am dumping the environment immediately before the EXIT /B in script2.bat and immediately after the CALL statement in script1.bat. The variables exist in the first dump. The variables do not exist in the second dump. (There are many other dumps...) – lit Jun 03 '14 at 23:48
  • @Paul - Did you add a bunch of extra ENDLOCAL before the last dump in script2? Until you tell me you've done that, I will think there is still an active SETLOCAL for some as yet unknown reason in script2 on your machine. – dbenham Jun 04 '14 at 01:18
  • I have been through the code and paired up the setlocal and endlocal statements. But I will check again. Still, that should not cause it to work on one machine and not on another. – lit Jun 04 '14 at 03:35
  • 1
    I believe you when you say you matched up the SETLOCAL/ENDLOCAL. Never-the-less, I'd like you to temporarily add a bunch of extra ENDLOCAL because I think you will then lose your values prior to the EXIT /B. This will either prove I am right that you still have an active SETLOCAL, or prove me wrong. As to why you might have an active SETLOCAL only on one machine? - I couldn't possibly guess without seeing the code. But the first thing to do is verify that residual SETLOCAL is the problem (or not). Don't rely on your code parsing skills. Do the simple test. – dbenham Jun 04 '14 at 03:50
  • 1
    I added five (5) `ENDLOCAL` statements before `EXIT /B`. Same result. On XP the new variables are retained. On Windows 7 the new variables are discarded. The .bat script file is on a server. I am running it from the same location on XP and 7. I am not running a different .bat script file on each machine. – lit Jun 04 '14 at 14:50
  • @Paul - Did you add a test after the extra ENDLOCALs but before the EXIT /B to see if the variables still exist? I think you will find they are missing, in which case you will have to track down why some ENDLOCAL(s) is not firing as expected on your Win 7 machine. – dbenham Jun 04 '14 at 15:01
  • At the normal end of the script, the new variables are present. After the first `endlocal` test is done, the new variables are gone on Windows 7. However, on Windows XP the new variables still exist after all of the `ENDLOCAL` test statements. Why would that be? – lit Jun 04 '14 at 16:33
  • @Paul - Please carefuly reread my answer and comments. I've already said about all I can about that. – dbenham Jun 04 '14 at 21:59
1

Try this:

(
   ENDLOCAL
   SET "_Var1=Some Variable You want to exist"
   SET "_Var2=Some Other Variable You want to exist"
   EXIT /b 0
)

Also make sure you call Batch 2 from batch 1 like this:

CALL "\\PathToBatch2\Batch2.cmd"

ALTERNATELY you can do this:

CMD One:

REM Script: Batch1
@(
   SETLOCAL
   ECHO OFF
   SET "_CallBatch2=C:\PathToBatch2\Batch2.cmd"
   SET "_SetCmd=CALL :SetCMD "
   SET "_RecievedVarList="
   SET "_RecievedVar1=" & REM  -- Note only done to show this is being created, normally you won't know or care what variables are being returned.
   SET "_eLvL=0"
)

CALL :Main

(
   ENDLOCAL
   EXIT /b %_eLvl%
)

:Main
   FOR %%A IN (CALL "%_CallBatch2%") DO (
      IF /I "%%~A" EQU "SET" (
         REM CALL %%A "%%~B" would work too
         %_SetCmd% %%~B
      ) ELSE (
         REM Looks like this was intended to be some output, show it.
         ECHO.%%A %%B
      )
   )
   FOR /F "Tokens=1*" %%A IN (%_RecievedVarList%) DO (
      REM ECHO the Variable's name and it's contents:
      CALL ECHO."%%~A" = "%%%%~A%%"
   )
GOTO :EOF

:SetCMD
   SET "%*"
   FOR /F "Tokens=1 Delims==" %A IN ("%*") DO (
     REM Store vars to output later to check their values.
     SET "_RecievedVarList=%_RecievedVarList% "%A""
   )
GOTO :EOF

.

CMD Two:

REM Script: Batch2
@(
   SETLOCAL
   ECHO OFF
)
CALL :Main
(
   ENDLOCAL
   EXIT /b %_eLvl%
)
:Main
   ECHO.SET "_RecievedVar1=This is Recieved Var 1"
   ECHO.SET "_RecievedVar2=This is Recieved Var 2"
GOTO :EOF
Ben Personick
  • 3,074
  • 1
  • 22
  • 29