批量退出时隐式 "popd"
implicit "popd" on batch exit
有没有办法撤消脚本末尾的所有 pushd
。我拥有的是:
pushd somwhere
rem doing stuff
goto end
:end
popd
goto :EOF
我想要的是:
setlocal
pushd somwhere
rem doing stuff
goto :EOF
但这不起作用,目录保持“pushd”状态。另一个我不能说会发生多少 pushd
的尝试是:
set CurrentDir=%CD%
rem doing a lot of pushd and popd
pushd somwhere
:POPBACK
if /i not "%CD%" == "%CurrentDir%" popd & goto POPBACK
但看起来我很容易卡在 :POPBACK
。添加一个计数器以退出该循环有点不确定我最终会到达哪个目录。
在这种情况下,我想知道 a 是否可以看到 pushd 目录的堆栈。我唯一发现的是 prompt $P$+$G
中的 $+
,它为每个 pushd 目录添加了一个“+”,例如D:\CMD++>
。但我看不出如何利用它。
编辑:
我只是注意到 prompt
的 $+
有问题,这让我很困惑。
上例中的setlocal
其实是把脚本return转到了初始目录,但是提示显示一个'+'表示缺少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>
编辑:
由于提示本地环境和目录堆栈之间的差异,我扩展了上面的示例表单,在调用脚本之前推送到 C:\DATA。脚本 returns 到调用它的地方 (C:\DATA)。到目前为止,一切都很好。第一个 popd
显示没有效果,因为它从堆栈中删除了最后一个 pushd
但没有更改目录,因为目录更改已被(隐式)endlocal 撤消。第二个 popd
returns 到预期的初始目录。
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>
pushd
command without any arguments lists the contents of the directory stack, which can be made use of, by writing the list with output redirection >
to a (temporary) file, which is then read by a for /F
loop, and to determine how many popd
commands 是必须的。
pushd > "tempfile.tmp" && (
for /F "usebackq delims=" %%P in ("tempfile.tmp") do popd
del "tempfile.tmp"
)
直接在cmd:
pushd > "tempfile.tmp" && (for /F "usebackq delims=" %P in ("tempfile.tmp") do @popd) & del "tempfile.tmp"
请注意,您不能使用 for /F "delims=" %P in ('pushd') do ( … )
,因为 for /F
would execute pushd
in a new instance of cmd.exe
有自己的新 pushd
/popd
堆栈。
防止 pushd
/popd
堆栈中残留的一种方法是简单地避免 pushd
并在批处理文件中使用 cd /D
instead within a localised environment (by setlocal
/endlocal
,因为,与popd
、endlocal
会在必要时隐式执行(即使批处理文件包含导致中止执行的致命错误的错误语法,如 rem %~
)。这当然只有在没有要处理的UNC路径(即映射到本地路径)的情况下才有效。
建议仅在没有任何分支的有限代码部分(通过 if
、goto
等)使用 pushd
/popd
。此外,确保考虑到潜在的错误(在此处使用 conditional execution):
rem // First stack level:
pushd "D:\main" && (
rem // Second stack level:
pushd "D:\main\sub" && (
rem /* If `D:\main\sub` does not exist, `pushd` would obviously fail;
rem to avoid unintended `popd`, use conditional execution `&&`: */
popd
)
rem // We are still in directory `D:\main` here as expected.
popd
)
这里有两点:
1. Setlocal/Endlocal命令save/restore当前目录!
这意味着当执行Endlocal
命令时,当前目录返回到执行前一个Setlocal
命令时的位置。无论 Endlocal
是显式执行,还是通过终止被调用的子程序(通过 exit /B
或 goto :EOF
命令隐式执行),都会发生这种情况。
此行为可用于解决您的问题,因为当执行 endlocal
命令时,当前目录的所有更改 都将还原。即:
setlocal
cd somwhere
rem doing stuff
goto :EOF
但是请注意,此行为不与到pushd/popd
命令相关!您不能将此功能与 pushd/popd
命令混合使用。在理解这两个功能之间的交互机制之前,您需要完成多次测试。
这是讨论的未记录行为 here。
2。 POPD命令设置Exit Code当没有之前的PUSHD
如下所述退出代码管理部分,有一些命令在发生错误时不设置ErrorLevel,而是设置一个内部值我们可以调用“退出代码”。这个值可以通过 && ||
构造来测试:
command && Then command when Exit Code == 0 || Else command when Exit code != 0
POPD
是此类命令之一(在 Table 5 中有描述),因此我们可以使用此功能执行 POPD
多次,直到没有匹配的 PUSHD
之前执行:
@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
底线:一个简单的 popd && goto POPBACK
行就可以解决您的问题...
在我自己在现已删除的评论中提醒 exit codes
和@Aacini 发布的答案之后,又一个版本
@echo off
pushd "somewhere"
pushd "somewhere else"
:pop
popd && (echo %cd% & goto :pop) || goto :EOF
其中 echo %cd%
只会显示堆栈中的每个项目,因此可以删除并简单地变为:
:pop
popd && goto :pop || goto :EOF
有没有办法撤消脚本末尾的所有 pushd
。我拥有的是:
pushd somwhere
rem doing stuff
goto end
:end
popd
goto :EOF
我想要的是:
setlocal
pushd somwhere
rem doing stuff
goto :EOF
但这不起作用,目录保持“pushd”状态。另一个我不能说会发生多少 pushd
的尝试是:
set CurrentDir=%CD%
rem doing a lot of pushd and popd
pushd somwhere
:POPBACK
if /i not "%CD%" == "%CurrentDir%" popd & goto POPBACK
但看起来我很容易卡在 :POPBACK
。添加一个计数器以退出该循环有点不确定我最终会到达哪个目录。
在这种情况下,我想知道 a 是否可以看到 pushd 目录的堆栈。我唯一发现的是 prompt $P$+$G
中的 $+
,它为每个 pushd 目录添加了一个“+”,例如D:\CMD++>
。但我看不出如何利用它。
编辑:
我只是注意到 prompt
的 $+
有问题,这让我很困惑。
上例中的setlocal
其实是把脚本return转到了初始目录,但是提示显示一个'+'表示缺少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>
编辑:
由于提示本地环境和目录堆栈之间的差异,我扩展了上面的示例表单,在调用脚本之前推送到 C:\DATA。脚本 returns 到调用它的地方 (C:\DATA)。到目前为止,一切都很好。第一个 popd
显示没有效果,因为它从堆栈中删除了最后一个 pushd
但没有更改目录,因为目录更改已被(隐式)endlocal 撤消。第二个 popd
returns 到预期的初始目录。
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>
pushd
command without any arguments lists the contents of the directory stack, which can be made use of, by writing the list with output redirection >
to a (temporary) file, which is then read by a for /F
loop, and to determine how many popd
commands 是必须的。
pushd > "tempfile.tmp" && (
for /F "usebackq delims=" %%P in ("tempfile.tmp") do popd
del "tempfile.tmp"
)
直接在cmd:
pushd > "tempfile.tmp" && (for /F "usebackq delims=" %P in ("tempfile.tmp") do @popd) & del "tempfile.tmp"
请注意,您不能使用 for /F "delims=" %P in ('pushd') do ( … )
,因为 for /F
would execute pushd
in a new instance of cmd.exe
有自己的新 pushd
/popd
堆栈。
防止 pushd
/popd
堆栈中残留的一种方法是简单地避免 pushd
并在批处理文件中使用 cd /D
instead within a localised environment (by setlocal
/endlocal
,因为,与popd
、endlocal
会在必要时隐式执行(即使批处理文件包含导致中止执行的致命错误的错误语法,如 rem %~
)。这当然只有在没有要处理的UNC路径(即映射到本地路径)的情况下才有效。
建议仅在没有任何分支的有限代码部分(通过 if
、goto
等)使用 pushd
/popd
。此外,确保考虑到潜在的错误(在此处使用 conditional execution):
rem // First stack level:
pushd "D:\main" && (
rem // Second stack level:
pushd "D:\main\sub" && (
rem /* If `D:\main\sub` does not exist, `pushd` would obviously fail;
rem to avoid unintended `popd`, use conditional execution `&&`: */
popd
)
rem // We are still in directory `D:\main` here as expected.
popd
)
这里有两点:
1. Setlocal/Endlocal命令save/restore当前目录!
这意味着当执行Endlocal
命令时,当前目录返回到执行前一个Setlocal
命令时的位置。无论 Endlocal
是显式执行,还是通过终止被调用的子程序(通过 exit /B
或 goto :EOF
命令隐式执行),都会发生这种情况。
此行为可用于解决您的问题,因为当执行 endlocal
命令时,当前目录的所有更改 都将还原。即:
setlocal
cd somwhere
rem doing stuff
goto :EOF
但是请注意,此行为不与到pushd/popd
命令相关!您不能将此功能与 pushd/popd
命令混合使用。在理解这两个功能之间的交互机制之前,您需要完成多次测试。
这是讨论的未记录行为 here。
2。 POPD命令设置Exit Code当没有之前的PUSHD
如下所述退出代码管理部分&& ||
构造来测试:
command && Then command when Exit Code == 0 || Else command when Exit code != 0
POPD
是此类命令之一(在 Table 5 中有描述),因此我们可以使用此功能执行 POPD
多次,直到没有匹配的 PUSHD
之前执行:
@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
底线:一个简单的 popd && goto POPBACK
行就可以解决您的问题...
在我自己在现已删除的评论中提醒 exit codes
和@Aacini 发布的答案之后,又一个版本
@echo off
pushd "somewhere"
pushd "somewhere else"
:pop
popd && (echo %cd% & goto :pop) || goto :EOF
其中 echo %cd%
只会显示堆栈中的每个项目,因此可以删除并简单地变为:
:pop
popd && goto :pop || goto :EOF