【问题标题】:Batch file not keeping variables批处理文件不保留变量
【发布时间】:2016-09-21 21:21:15
【问题描述】:

为什么CMD.exe终止时下面的批处理文件不保留变量

@echo off
cmd /c "set var=hi"
if defined var (echo var is defined ("var=%var%")) else (echo var isn't defined)
pause
exit

有什么方法可以使用 CMD /c,同时保留变量?为什么 CMD /c 不保留变量?

【问题讨论】:

  • 你为什么使用cmd /c "set var=hi"?这将在子流程中创建变量,并且变量将随之消失。
  • 感谢您使用 MCVE(说真的,这在这个标签上非常罕见),但您到底为什么要设置这样的变量?
  • @SomethingDark,这只是一个例子。在实际使用中,我会在批处理文件中执行call cmd /c "%~0",然后当批处理文件退出时,它将返回到主脚本。这是我能想到的“阻止”批处理文件终止自身的唯一方法。此外,如果 CMD /c 保留变量,那么它将允许脚本的其余部分访问它设置的变量。长解释soz
  • 既然%0 是批处理文件本身,为什么不只是call "%~0"
  • @SomethingDark 如果批处理文件崩溃,例如。将 |& 放入批处理文件,然后它只是终止父窗口,即使它已被调用。如果您使用call cmd /c "%~0",它只会终止子 CMD 进程,而不是父进程。如果你明白我的意思,它有点沙盒

标签: variables batch-file cmd


【解决方案1】:

CMD /C 启动一个新的子进程,该子进程有自己的环境空间,变量存储在内存中。当该进程终止时,相关的环境空间就会丢失,因此您当然会丢失在那里定义的所有变量的定义。

子进程无法直接访问或操作父进程的变量。

如果您正在执行一个简单的命令并且可以控制任何输出,那么您可以将定义写入标准输出并让 FOR /F 捕获输出,以便您可以在父进程中设置值。但是您需要延迟扩展。

注意 - 这个例子很空洞,但它应该能说明问题

for /f "delims=" %%A in ('cmd /v:on /c "set var=hi&echo "var=!var!""') do set %%A

但就识别引用的内容、需要转义的内容而言,事情可能会变得复杂......

如果您创建一个批处理脚本来完成这项工作并使用 FOR /F 执行它,那么可以简化生活。

test.bat

@echo off
set var1=hi
set var2=Ain't this fun
set var3=bye
setlocal enableDelayedExpansion
for %%V in (var1 var2 var3) do echo "%%V=!%%V!"

ma​​in.bat

@echo off
for /f "delims=" %%A in ('cmd /c test.bat') do set %%A

其实 FOR /F 使用的是隐式的 CMD /C,你不再需要在命令中开启延迟扩展,所以你可以抛弃显式的 CMD /C

@echo off
for /f "delims=" %%A in ('test.bat') do set %%A

如果您无法控制输出,则必须将值写入一个临时文件,然后主脚本可以加载该文件。您可能很想编写一个定义变量的临时批处理脚本,但是根据变量的内容,您可能不得不转义某些字符。最安全的选择是使用延迟扩展写出变量名和值,让父进程使用 FOR /F 加载进来。

test2.bat

@echo off
set var1=hi
set var2=Ain't this fun
set var3=bye
echo Here is some output that must be preserved, unrelated to variables
setlocal enableDelayedExpansion
(for %%V in (var1 var2 var3) do echo "%%V=!%%V!") >"%temp%\vars.temp"

ma​​in.bat

@echo off
setlocal disableDelayedExpansion
cmd /c test2.bat
call "%temp%\vars.bat"
for /f "usebackq delims=" %%V in ("%temp%\vars.temp") do set "%%V"
del "%temp%\vars.temp"

注意,在加载变量时,必须在主进程中禁用延迟扩展,否则在扩展%%V时会丢失值中的任何!字符。

但为了稳健,您应该采取措施确保每个实例化使用唯一的临时文件名,以便同时运行不会相互干扰。

【讨论】:

  • 在实际使用中,我会在一个批处理文件中调用cmd /c "%~0",然后当批处理文件退出时,它会返回到主脚本。这是我能想到的“阻止”批处理文件终止自身的唯一方法。此外,如果 CMD /c 保留变量,那么它将允许脚本的其余部分访问它设置的变量。你能想到任何其他的解决方案,可以允许批处理文件调用它自己,同时保持它的环境空间?
  • @BurnieRiley - 只需在脚本开头添加一个setlocal,一旦脚本终止,所有在脚本中设置的变量都将被取消设置。
猜你喜欢
  • 1970-01-01
  • 2015-03-23
  • 1970-01-01
  • 2018-03-25
  • 2010-10-23
  • 2018-01-14
  • 1970-01-01
  • 2016-06-17
  • 1970-01-01
相关资源
最近更新 更多