为什么在Windows批处理文件中使用setlocal会干扰chdir?

9
如果我运行批处理文件
setlocal
chdir ..

目录没有改变,但是如果我运行
setlocal
endlocal 
chdir ..

程序正常工作。这正是使用setlocal所期望的。然而,当你阅读setlocal的定义时,它与环境变量的查看方式有关,并不完全明显。我希望这是一个好机会来解释setlocal的实际作用以及为什么它会干扰chdir。

2个回答

7
SETLOCAL命令的HELP文档(help setlocalsetlocal /?)实际上已经很好地解释了情况。唯一不明显的是,“环境变量的本地化更改”不仅包括环境变量,还包括当前目录和延迟扩展以及扩展状态。可能还有更多,但我现在想不起来了。
让你困惑的事情实际上已经很好地解释了:“当批处理脚本结束时,任何由该批处理脚本发出的未完成SETLOCAL命令都会执行隐含的ENDLOCAL。”没有说明的是对被调用子程序也同样适用。
当你的批处理脚本结束时,隐含的ENDLOCAL“抹去”了CHDIR的影响。你第二个代码中的显式ENDLOCAL将你带回到根环境,所以你的CHDIR就被保留了。

更新

当前目录不是一个环境变量,即使你通常可以使用%CD%获取当前值。你可以通过尝试SET CD来证明它 - 它可能会给你“未定义环境变量CD”的信息。如果你使用set "CD=some value"明确定义你自己的真正CD变量,那么%CD%将返回你分配的值,而不是当前目录。
在旧的COMMAND.COM时代,原始的SETLOCAL命令没有控制延迟扩展或扩展。当CMD.EXE被引入时,启用/禁用延迟扩展和启用/禁用扩展选项被添加。这只是微软决定实现该功能的方式。它不必是这样的。在很多方面,不能在不本地化环境的情况下控制这些状态是不幸的。我经常希望能够在不本地化环境的情况下启用或禁用延迟扩展。

有两个关键点:当前目录是一个环境变量,脚本末尾隐含的ENDLOCAL会返回原始环境。现在我更好地理解了setlocal,但我不太清楚为什么enabledelayedexpansion必须与setlocal一起使用。这是另一个问题,但它解释了为什么我迷失了方向:我的经验是setlocal用于改变变量的计算方式,我看不到它们之间的联系。 - user2066805
好的更新。我没有想到当前目录%CD%可以通过SET进行更改,而只是它是环境的一部分的变量。 - user2066805
伟大的额外点,实际上也是我提问的来源,就是这个声明:原则上,我们不需要本地化环境来启用延迟扩展。 - user2066805
1
@Dominic108 - 我忘记了 %__CD__%,它与 %CD% 的唯一区别是末尾的 \。此外,您可以定义一个真正的 __CD__ 环境变量,但无法访问它 - %__CD__% 始终返回当前目录并带有末尾的 \,即使已定义 __CD__ 变量。 - dbenham
1
解释得很好,但关于“环境变化的本地化”对回声状态的影响似乎是错误的。 - jeb
显示剩余3条评论

1
我遇到了这个问题,正在编写一个批处理文件,根据命令行参数和一堆内部逻辑改变目录。
使用setlocal和更改目录的一种方法是递归调用批处理文件,将目标路径作为字符串返回,并让顶层调用者使用cd
在这个例子中,批处理文件接受命令行上的ppx来更改到特定的目录:
@echo off
if not "%1"=="recurse" (
    for /f "delims=" %%i in ('%0 recurse %1') do cd %%i
    exit /b
)
setlocal
rem Do things here with "local" variables
if "%2"=="p" (
    echo "c:\program files"
) else if "%2"=="px" (
    echo "c:\program files (x86)"
)

注:

  • 我随意选择了字符串“recurse”。
  • delims设置为空,这样它就不会在路径中的空格处断开返回的字符串。
  • %0返回要递归的批处理文件的路径。

感谢SO Bash反引号的批处理等效方法


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