Question

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.

Was it helpful?

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top