Why setlocal interferes with chdir in windows batch files?

孤街醉人 提交于 2020-06-08 16:50:10

问题


If I run the batch file

setlocal
chdir ..

the directory is not changed, but if I run

setlocal
endlocal 
chdir ..

it works normally. This must be exactly what is expected with setlocal. However, it is not entirely obvious when you read the definition of setlocal, which is related to how the environment variables are seen. I am hoping that this is a good occasion to explain what setlocal actually does and why it interferes with chdir.


回答1:


The HELP documentation for SETLOCAL (help setlocal or setlocal /?) actually explains the situation pretty well. The only thing that is not obvious is that "localization of environment changes" doesn't just include environment variables, but also includes the current directory, and the delayed expansion and extensions states. There may be more, but I can't think of it at the moment.

The thing that is tripping you up is actually explained fairly well: "When the end of a batch script is reached, an implied ENDLOCAL is executed for any outstanding SETLOCAL commands issued by that batch script." Not stated is that the same is true for called subroutines.

When your batch script ends, the implicit ENDLOCAL "erases" the effect of your CHDIR. Your explicit ENDLOCAL in your second code gets you back to the root environment, so your CHDIR is then preserved.


Update

The current directory is not an environment variable, even though you can normally get the current value using %CD%. You can prove it by tring SET CD - it will probably give you "Environment variable CD not defined". If you explicitly define your own true CD variable using set "CD=some value", then %CD% will return the value you assigned, and not the current directory.

The original SETLOCAL command did not control delayed expansion or extensions back in the old COMMAND.COM days. The Enable/Disable DelayedExpansion and Enable/Disable Extensions options were added when CMD.EXE was introduced. It's just how MS decided to implement the feature. It didn't have to be that way. In many ways it is unfortunate that you cannot control those states without SETLOCAL/ENDLOCAL. I often wish I could enable or disable delayed expansion without localizing the environment.




回答2:


I ran into this behavior writing a batch file to change directories based on command-line parameters and a bunch of internal logic.

One way to use setlocal and change directories is to call the batch file recursively, return the destination path as a string, and have the top-level caller cd to it.

In this example the batch file takes either p or px on the command line to change to a particular directory:

@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)"
)

Notes:

  • I chose the string "recurse" arbitrarily.
  • delims is set to nothing so it doesn't break the returned string on spaces in the path.
  • %0 returns the path to the batch file to recurse.

Thanks to SO Batch equivalent of Bash backticks.



来源:https://stackoverflow.com/questions/20153956/why-setlocal-interferes-with-chdir-in-windows-batch-files

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!