Discussion:
setlocal does pushd/popd?
(too old to reply)
charles
2005-10-29 15:33:11 UTC
Permalink
With the following code, setlocal is changing to a target directory then
returning to where it started as if a pushd/popd pair was used. I can't
find any documentation that it should do this (and its driven me crazy
until I thought to include a test in the fragment).

I'm just wondering whether anyone has noticed it/seen anything similar
before.

(Using windows 2000)

==== begin code ====
@echo off&setlocal
rem Function of (maybe) 2 args: 1 = param, 2 = directory
if x%1 == x set blockset=final &goto ENDA1
2>NUL cd /d %1 && (set blockset=final &goto ENDP)
if %1 == early set blockset=%1 &goto ENDA1
if %1 == middle set blockset=%1 &goto ENDA1
echo Unknown blockset mode, using default=final. &set blockset=final
:ENDA1
if not x%2 == x cd /d %2 || echo Unfound directory, can't CD to %2.
rem if errorlevel 1 echo something else
:ENDP
rem test for file in dir.
dir atools.bat
echo Blockset=%blockset%
==== end code ====
William Allen
2005-10-29 16:24:22 UTC
Permalink
"charles" wrote in message
Post by charles
With the following code, setlocal is changing to a target directory then
returning to where it started as if a pushd/popd pair was used. I can't
find any documentation that it should do this (and its driven me crazy
until I thought to include a test in the fragment).
I'm just wondering whether anyone has noticed it/seen anything similar
before.
SETLOCAL doesn't change the current folder, but I'd noticed that SETLOCAL
localises the effect of any CD commands in the same way as it localises
environment variable changes, so their effect ceases with the following
ENDLOCAL (or the implied ENDLOCAL at the end of the Batch file).
This applies to nested SETLOCAL commands, too, giving rise to
nesting of CD command effects, thus:

CD C:\InitialFolder
:: C:\InitialFolder is now current
SETLOCAL
CD C:\Folder1
:: C:\Folder1 is now current at SetLocal depth 1
SETLOCAL
CD C:\Folder2
:: C:\Folder2 is now current at SetLocal depth 2
ENDLOCAL
:: C:\Folder1 is once again current now we're back at SetLocal depth 1
ENDLOCAL
:: C:\InitialFolder is now current again with no localisation in force

General idea tested in Win95cmd.exe (Win2000 emulator under
Windows 95/98/ME)

--
William Allen
Free interactive Batch Course http://www.allenware.com/icsw/icswidx.htm
Batch Reference with examples http://www.allenware.com/icsw/icswref.htm
From email address not checked. Contact us at http://www.allenware.com/
charles
2005-10-31 12:59:56 UTC
Permalink
Post by William Allen
"charles" wrote in message
Post by charles
With the following code, setlocal is changing to a target directory then
returning to where it started as if a pushd/popd pair was used. I can't
find any documentation that it should do this (and its driven me crazy
until I thought to include a test in the fragment).
I'm just wondering whether anyone has noticed it/seen anything similar
before.
SETLOCAL doesn't change the current folder, but I'd noticed that SETLOCAL
localises the effect of any CD commands in the same way as it localises
environment variable changes, so their effect ceases with the following
ENDLOCAL (or the implied ENDLOCAL at the end of the Batch file).
This applies to nested SETLOCAL commands, too, giving rise to
CD C:\InitialFolder
:: C:\InitialFolder is now current
SETLOCAL
CD C:\Folder1
:: C:\Folder1 is now current at SetLocal depth 1
SETLOCAL
CD C:\Folder2
:: C:\Folder2 is now current at SetLocal depth 2
ENDLOCAL
:: C:\Folder1 is once again current now we're back at SetLocal depth 1
ENDLOCAL
:: C:\InitialFolder is now current again with no localisation in force
General idea tested in Win95cmd.exe (Win2000 emulator under
Windows 95/98/ME)
Well it's neat that it works the way it does and we can let this little
thread be the documentation that MS never published.
Nathan Phillip Brink
2022-06-22 14:45:56 UTC
Permalink
An important observation that I have made is that SETLOCAL/ENDLOCAL does not restore the PUSHD/POPD stack. So if you have a script which uses SETLOCAL and PUSHD, the script will still insert the working directory at the time of the PUSHD into the stack. This disturbs the caller’s state, which you are unlikely to want if you are using SETLOCAL. Thus, I recommend to use a pattern where you script begins with SETLOCAL and then you simply use CD in the script. This way, no matter how the script exits, the caller’s working directory will be restored and the caller’s PUSHD/POPD stack will not be altered.

To see the effects of SETLOCAL followed by PUSHD without any POPD, see this following transcript:

C:\Users\ohnob>TYPE AppData\Local\Temp\xx1.cmd
@ECHO OFF
SETLOCAL
PUSHD %~dp0 || EXIT /B 1

C:\Users\ohnob>PUSHD ..

C:\Users>PUSHD ..

C:\>"%USERPROFILE%\AppData\Local\Temp\xx1.cmd"

C:\>CD "%USERPROFILE%"

C:\Users\ohnob>POPD

C:\>POPD

C:\Users>POPD

C:\Users\ohnob>POPD

C:\Users\ohnob>POPD

You can see that the first POPD I run returns me to the root directory even though I never called PUSHD from that directory. The following is the preferred pattern:

@ECHO OFF
SETLOCAL
CD %~dp0 || EXIT /B 1

Loading...