Is there a way to undo all pushd
at the end of script. What I have is:
pushd somwhere
rem doing stuff
goto end
:end
popd
goto :EOF
What I'd like to have is:
setlocal
pushd somwhere
rem doing stuff
goto :EOF
But that doesn't work, the directory stays "pushd". Another try where I can't say how many pushd
will occur would be:
set CurrentDir=%CD%
rem doing a lot of pushd and popd
pushd somwhere
:POPBACK
if /i not "%CD%" == "%CurrentDir%" popd & goto POPBACK
But that looks like I can easily get stuck at :POPBACK
. Adding a counter to exit that loop is a bit uncertain at which dir I end up.
In that context I'd like to know if a can see the stack of pushd directories. The only thing I found was $+
in prompt $P$+$G
which adds a '+' for each pushd directory e.g. D:\CMD++>
. But I can't see a way how to make use of that.
EDIT:
I just noticed there is something wrong with the $+
of prompt
that confused me.
The setlocal
in the example above actually makes the script return to the initial directory, but the prompt shows a '+' indicating a missing popd
.
D:\CMD>test.cmd
D:\CMD>prompt $P$+$g
D:\CMD>setlocal
D:\CMD>pushd e:\DATA
e:\DATA+>goto :EOF
D:\CMD+>popd
D:\CMD>
EDIT:
Due to the hint to the difference between local environment and directory stack I expanded the example form above, pushing to C:\DATA before calling the script. the script returns to where it has been called from (C:\DATA). So far so good. The first popd
shows no effect as it removes the last pushd
from stack but not changing the directory, since the directory change has been undone by the (implicit) endlocal. The second popd
returns to initial directory as expected.
D:\CMD>pushd C:\DATA
C:\DATA+>d:\cmd\test.cmd
C:\DATA+>prompt $P$+$g
C:\DATA+>setlocal
C:\DATA+>pushd e:\DATA
e:\DATA++>goto :EOF
C:\DATA++>popd
C:\DATA+>popd
D:\CMD>
Two points here:
1. Setlocal/Endlocal commands save/restore current directory!
This means that when Endlocal
command is executed, the current directory is returned to the point it has when the previous Setlocal
command was executed. This happens no matter if the Endlocal
is explicitly executed, or it is implicitly executed by the termination of the called subroutine (via exit /B
or goto :EOF
commands).
This behaviour could be used to solve your problem, because all changes of the current directory are reverted when endlocal
command is executed. That is:
setlocal
cd somwhere
rem doing stuff
goto :EOF
However note that this behavior is not related to pushd/popd
commands! You can not mix the use of this feature with pushd/popd
commands. You would need to complete several tests until the mechanism of the interaction between these two features be comprehended.
This is an undocumented behavior discussed here.
2. POPD command sets the Exit Code when there is no previous PUSHD
As described below Exit Code management section at this answer, there are some commands that does not set the ErrorLevel when an error happens, but sets an internal value we could call "Exit Code". This value can be tested via the && ||
construct this way:
command && Then command when Exit Code == 0 || Else command when Exit code != 0
POPD
is one of this type of commands (described under Table 5), so we could use this feature to execute POPD
several times until there is not a matching PUSHD
previously executed:
@echo off
setlocal
echo Enter in:
for /L %%i in (1,1,%1) do echo %%i & pushd ..
echo Leave out:
set "n=0"
:nextLevel
popd || goto exitLevels
set /A n+=1
echo %n%
goto nextLevel
:exitLevels
echo Done
Bottom line: a simple popd && goto POPBACK
line solves your problem...