3

有没有办法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. 添加一个计数器以退出该循环有点不确定我最终会在哪个目录。

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

编辑:我只是注意到这让我感到困惑$+promptsetlocal例中的实际使脚本返回到初始目录,但提示符显示一个 '+' 表示缺少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。脚本返回到从 (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>
4

3 回答 3

4

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

pushd > "tempfile.tmp" && (
    for /F "usebackq delims=" %%P in ("tempfile.tmp") do popd
    del "tempfile.tmp"
)

直接在中:

pushd > "tempfile.tmp" && (for /F "usebackq delims=" %P in ("tempfile.tmp") do @popd) & del "tempfile.tmp"

请注意,您不能使用for /F "delims=" %P in ('pushd') do ( … ), 因为for /Fpushd在 的新实例中执行,该实例cmd.exe有自己的新pushd/popd堆栈。


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


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

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
)
于 2022-02-09T09:31:40.547 回答
3

这里有两点:

1. setlocal/Endlocal 命令保存/恢复当前目录!

这意味着当Endlocal执行命令时,当前目录将返回到Setlocal执行前一个命令时所在的位置。无论Endlocal是显式执行,还是通过被调用子程序的终止(通过exit /Bgoto :EOF命令)隐式执行,都会发生这种情况。

此行为可用于解决您的问题,因为当前目录的所有更改都会endlocal在执行命令时还原。那是:

setlocal
cd somwhere
rem doing stuff
goto :EOF

但是请注意,此行为命令无关pushd/popd!您不能将此功能与pushd/popd命令混合使用。您需要完成几个测试,直到理解这两个功能之间的交互机制。

这是此处讨论的未记录行为。


2、POPD命令在没有前一次PUSHD时设置Exit Code

正如下面这个答案的退出代码管理部分所述,有一些命令在发生错误时不会设置 ErrorLevel,而是设置一个我们可以称为“退出代码”的内部值。可以通过以下方式通过构造测试此值:&& ||

command && Then command when Exit Code == 0 || Else command when Exit code != 0

POPD是这种类型的命令之一(在表 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线可以解决您的问题...

于 2022-02-09T14:24:53.843 回答
0

在我自己exit codes在一个现已删除的评论中提醒和@Aacini 发布的答案之后,还有一个版本

@echo off
pushd "somewhere"
pushd "somewhere else"

:pop
popd && (echo %cd% & goto :pop) || goto :EOF

Whereecho %cd%只会显示堆栈中的每个项目,因此可以删除并简单地变为:

:pop
popd && goto :pop || goto :EOF
于 2022-02-09T14:56:22.940 回答