在Windows 7中,CALL批处理文件环境变量被丢弃

6
我成功地使用了 CALL 机制允许一个批处理文件调用另一个批处理来设置环境变量。这段代码在 Windows XP 上运行良好一年有余。
然而,在 Windows 7 上似乎没有以相同的方式工作。这些变量存在于第二个批处理文件中,就在 EXIT /B 语句之前。但是,在返回到第一个批处理文件时,它们不存在。
一些简单的示例似乎按预期工作,但是大型批处理脚本并不如此。
有人遇到过这个问题或知道任何解决方法吗?

3
请确保第二个批处理文件中没有setlocal命令。 - Aacini
第二个批处理脚本确实有几个 SETLOCAL 命令。我使用 ENDLOCAL 来确保在本地范围结束后设置本地变量。 - lit
1
我需要在第二个批处理文件中使用SETLOCAL以便使用ENABLEDELAYEDEXPANSION - lit
听起来试错调试可能是唯一的解决方法。删除代码部分,直到问题消失,然后缩小到确切的触发点。(特别是找出是否删除所有对SETLOCAL的调用会使问题消失。) - Harry Johnston
另外,在第二个批处理文件的结尾处加上pause,然后仔细检查只有一个cmd.exe实例正在运行。哦,你可以尝试将exit /b更改为goto :eof - 这可能不太可能,但也无妨。 :-) - Harry Johnston
1
可能是以下问题的重复:如何使环境变量在ENDLOCAL后依然存在 - ivan_pozdeev
2个回答

10
在多年的批处理脚本开发中,我从未见过CALL不能保留环境变量,除非被调用的脚本(或标签)在SETLOCAL仍处于活动状态时设置了该变量。对于从CALL内部调用终止的每个活动SETLOCAL,都隐含着一个ENDLOCAL。
听起来像是您在EXIT /B之前放置了诊断消息以确认您的变量已定义。我建议在诊断消息之前添加多个ENDLOCAL语句。我猜您将会在EXIT /B之前看到这些变量的值消失。您可以添加任意数量的ENDLOCAL,但ENDLOCAL不会影响在CALL之前出现的SETLOCAL。
最有可能的解释是您的脚本从XP更改为Win 7,或者您的Win 7环境中存在某种上下文变化,正在执行以前没有暴露的代码方面。

@Paul - 你在 script2 的最后一个 dump 前面添加了一堆额外的 ENDLOCAL 吗?除非你告诉我你已经这样做了,否则我会认为在你的机器上 script2 中仍然存在某些尚未知原因的活动 SETLOCAL。 - dbenham
我已经仔细查看了代码,并配对了setlocal和endlocal语句。但我会再次检查。不过,这不应该导致它在一台机器上工作而在另一台机器上不工作。 - lit
1
我相信你说的SETLOCAL/ENDLOCAL已经匹配了。尽管如此,我希望你暂时添加一堆额外的ENDLOCAL,因为我认为在EXIT /B之前你会失去你的值。这要么证明我是对的,你仍然有一个活动的SETLOCAL,要么证明我是错的。至于为什么你可能只在一台机器上有一个活动的SETLOCAL? - 如果没有看到代码,我不可能猜测。但首先要做的是验证残留的SETLOCAL是否是问题(或者不是)。不要依赖于你的代码解析技能。做一个简单的测试。 - dbenham
1
我在“EXIT /B”之前添加了五个(5)个“ENDLOCAL”语句。结果相同。在XP上,新变量被保留。在Windows 7上,新变量被丢弃。.bat脚本文件位于服务器上。我在XP和7上从相同位置运行它。我没有在每台机器上运行不同的.bat脚本文件。 - lit
@Paul - 你在额外的ENDLOCAL之后但是在EXIT /B之前添加了测试来查看变量是否仍然存在吗?我认为你会发现它们已经丢失了,如果是这样,那么你将不得不追踪为什么某些ENDLOCAL没有按预期在你的Win 7机器上触发。 - dbenham
显示剩余3条评论

1

试试这个:

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

同时,请确保您按照以下方式从批次1调用批次2:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接