批处理退出时隐式“popd”

ooL*_*oLi 3 windows cmd batch-file pushd

pushd有没有办法在脚本末尾撤消所有内容。我所拥有的是:

pushd somwhere
rem doing stuff
goto end

:end
popd
goto :EOF
Run Code Online (Sandbox Code Playgroud)

我想要的是:

setlocal
pushd somwhere
rem doing stuff
goto :EOF
Run Code Online (Sandbox Code Playgroud)

但这不起作用,目录保持“pushd”状态。我无法说出pushd会发生多少次的另一种尝试是:

set CurrentDir=%CD%
rem doing a lot of pushd and popd
pushd somwhere

:POPBACK
if /i not "%CD%" == "%CurrentDir%" popd & goto POPBACK
Run Code Online (Sandbox Code Playgroud)

但这看起来我很容易陷入困境:POPBACK。添加一个计数器来退出该循环有点不确定我最终会在哪个目录。

在这种情况下,我想知道 a 是否可以看到 Pushd 目录的堆栈。我发现的唯一的事情是$+prompt $P$+$G每个 Pushd 目录添加一个“+”,例如D:\CMD++>。但我看不出如何利用它。

编辑:我刚刚注意到有一些问题让$+prompt困惑。上面示例setlocal中的实际上使脚本返回到初始目录,但提示符显示一个“+”,表示缺少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>
Run Code Online (Sandbox Code Playgroud)

编辑:由于提示本地环境和目录堆栈之间的差异,我扩展了上面的示例表单,在调用脚本之前推送到 C:\DATA。脚本返回到调用它的位置 (C:\DATA)。到目前为止,一切都很好。第一个popd没有显示任何效果,因为它从堆栈中删除了最后一个pushd,但没有更改目录,因为目录更改已被(隐式)endlocal 撤消。第二个popd按预期返回到初始目录。

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>
Run Code Online (Sandbox Code Playgroud)

asc*_*pfl 5

\n

不带任何参数的命令列出目录堆栈的内容,可以通过将带有输出重定向的列表写入(临时)文件来使用目录堆栈的内容pushd,然后通过循环读取该文件,并确定需要多少命令>for /Fpopd

\n

中:

\n
pushd > "tempfile.tmp" && (\n    for /F "usebackq delims=" %%P in ("tempfile.tmp") do popd\n    del "tempfile.tmp"\n)\n
Run Code Online (Sandbox Code Playgroud)\n

直接在中:

\n
pushd > "tempfile.tmp" && (for /F "usebackq delims=" %P in ("tempfile.tmp") do @popd) & del "tempfile.tmp"\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,您不能使用for /F "delims=" %P in (\'pushd\') do ( \xe2\x80\xa6 ),因为它将在 的新实例中for /F执行,该实例有自己的新/堆栈。pushdcmd.exepushdpopd

\n
\n

防止pushd/popd堆栈残留的一种方法是简单地避免并在本地化环境中pushd使用(通过批处理文件中的/ ),因为与 不同的是,它会在必要时隐式执行(即使批处理文件包含导致错误的语法)导致中止执行的致命错误,例如)。当然,这仅在没有要处理的 UNC 路径(即映射到本地路径)时才有效。cd /Dsetlocalendlocalpopdendlocalrem %~

\n
\n

建议仅在有限的代码段中使用pushd/,而不使用任何分支(by 、等)。此外,请确保考虑到潜在的错误(此处使用条件执行):popdifgoto

\n
rem // First stack level:\npushd "D:\\main" && (\n    rem // Second stack level:\n    pushd "D:\\main\\sub" && (\n        rem /* If `D:\\main\\sub` does not exist, `pushd` would obviously fail;\n        rem    to avoid unintended `popd`, use conditional execution `&&`: */\n        popd\n    )\n    rem // We are still in directory `D:\\main` here as expected.\n    popd\n)\n
Run Code Online (Sandbox Code Playgroud)\n