batch-filechdirdelayedvariableexpansion

Why setlocal interferes with chdir in windows batch files?


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.


Solution

  • 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.