Windows批量setlocal enabledelayedexpansion调用返回值

lit*_*lit 0 batch-file environment-variables

有两(2)个Windows批处理脚本.第一个将调用第二个.但是,必须在调用第二个批处理脚本时使用SETLOCAL,因为FOR循环中需要ENABLEDELAYEDEXPANSION.

=== batch1.bat

@echo off
echo in %0
set BAT1_VAR=5
SETLOCAL
    call  batch2.bat
    set|find "BAT"
    echo === before endlocal
ENDLOCAL
set|find "BAT"
echo === after endlocal
echo === begin exiting %0
set|find "BAT"
echo === end exiting %0
exit /B 0
Run Code Online (Sandbox Code Playgroud)

=== batch2.bat

@echo off
echo in %0
SET BAT2_VAR=7
echo === begin exiting %0
SET|FIND "BAT"
echo === end exiting %0
EXIT /B 0
Run Code Online (Sandbox Code Playgroud)

===问题是batch2.bat设置的变量在返回batch1.bat后仍然需要存在.这可以通过使用以下方法解决:

ENDLOCAL & set BAT2_VAR=%BAT2_VAR%
Run Code Online (Sandbox Code Playgroud)

但是,我不希望batch1.bat需要知道batch2.bat创建的变量的名称.可能有很多,列表可能会更改.

有什么想法可以克服这个问题?谢谢.

dbe*_*ham 8

如果您处于循环或任何带括号的代码块中,则无法使用该ENDLOCAL & set BAT2_VAR=%BAT2_VAR%方法.

这样的事情会失败

REM delayed expansion is disabled to start
for ... %%A in (....) do (
   REM must SET before enabling delayed expansion to preserve ! in %%A value
   set var=%%A
   REM the remainder of the loop needs delayed expansion
   setlocal enableDelayedExpansion
   REM do something with !var!
   call batch2 !var!
   REM maybe the batch2 modifies var some more
   REM Must endlocal and preserve the value. The attempt below fails because %var% will
   REM be the value that existed before the loop was entered
   endlocal & set "var=%var%"
)
Run Code Online (Sandbox Code Playgroud)

一个简单的解决方法是通过FOR变量传输值

REM delayed expansion is disabled to start
for ... %%A in (....) do (
   REM must SET before enabling delayed expansion to preserve ! in %%A value
   set var=%%A
   REM the remainder of the loop needs delayed expansion
   setlocal enableDelayedExpansion
   REM do something with !var!
   call batch2 !var!
   REM maybe the batch2 modifies var some more
   REM the code below successfully transports the value across the endlocal barrier
   for /f "delims=" %%B in ("!var!") do endlocal & set "var=%%B"
)
Run Code Online (Sandbox Code Playgroud)

您说您的批次可能设置了您需要保留的许多值.通常,您将返回变量的名称传递给CALLed例程,然后您的例程可以执行以下操作:

set "%~2=return value1"
set "%~3=return value2"
... etc.
Run Code Online (Sandbox Code Playgroud)

您可能知道所有变量的名称,但这可能需要大量代码来传输跨地方障碍的所有值.

您可以使用公共前缀为所有变量添加前缀,然后使用FOR/F循环来保留值.在任何迭代开始之前,都会缓存FOR/F('command')的整个结果集.

您的CALLed批处理可以设置变量prefix.var1,, prefix.var2... prefix.varN,以下代码可以正常工作

REM delayed expansion is disabled to start
for ... %%A in (....) do (
   REM must SET before enabling delayed expansion to preserve ! in %%A value
   set var=%%A
   REM the remainder of the loop needs delayed expansion
   setlocal enableDelayedExpansion
   REM do something with !var!
   call batch2 !var! prefix.
   REM the batch2 set variables prefixed by prefix.
   REM Must endlocal and preserve all values.
   for /f "delims=" %%B in ('set prefix.') do (
     if "!"=="" endlocal
     set "%%B"
   )
)
Run Code Online (Sandbox Code Playgroud)

if "!"=="" endlocal行是一个非常好的技巧,只能在第一个内循环迭代中进行endlocal.它依赖于在外部循环之前禁用延迟扩展的事实,并且在每个外部循环迭代的顶部附近启用.