为什么 %CD% 在 PushD 的 for 循环中不起作用

Why does %CD% not work in a for loop with PushD

我想要 运行 一个 .bat 脚本,它创建一个文件并使用与它所在的文件夹相同的名称保存它。我希望它在地图中开始并循环遍历子地图。 我从下面的这段代码开始,运行 它在其中一个子图中。行得通,我得到了一个带有子图名称的文件。

FOR /D %%I IN ("%CD%") DO SET _LAST_SEGMENT_=%%~nxI
ECHO Last segment = "%_LAST_SEGMENT_%"
echo.>%_LAST_SEGMENT_%.mp4
pause

所以现在我只需要将它放在一个 for 循环中。所以我做了下面的代码,但它不起作用。 %CD% 似乎是指我的父地图(子文件夹),.bat 文件所在的位置,而不是我的子地图(video1、video2 等)。

Set Base=C:\Video\subfolders
FOR /R "%Base%" %%F IN (.) DO (
    PushD "%%F"
        FOR /D %%I IN ("%CD%") DO SET _LAST_SEGMENT_=%%~nxI
        echo %CD%
        ECHO Last segment = "%_LAST_SEGMENT_%"
        echo.>%_LAST_SEGMENT_%.mp4
    PopD
)
Pause

输出:

C:\Video\subfolders>Set Base=C:\Video\subfolders
C:\Video\subfolders>FOR /R "C:\Video\subfolders" %F IN (.) DO (
PushD "%F"
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders>(
PushD "C:\Video\subfolders\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>(
PushD "C:\Video\subfolders\video1\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders\video1>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>(
PushD "C:\Video\subfolders\video2\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders\video2>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>(
PushD "C:\Video\subfolders\video3\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders\video3>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>Pause
Press any key to continue . . .

我看到下面的 link 解释了如何使用 setlocal enabledelayedexpansion,但我无法让它工作。

https://superuser.com/questions/503488/why-doesnt-cd-work-after-using-pushd-to-a-unc-path

如有任何建议,我们将不胜感激

看起来你想要这样的东西:

@echo off
for /F "delims=" %%I in ('dir "C:\Video\subfolders\*" /AD-H /B /S 2^>nul') do (
    pushd "%%I"
    echo/>"%%~nxI.mp4"
    popd
)

命令 pushdpopd 并不是真正必要的,如下面的代码所示:

@echo off
for /F "delims=" %%I in ('dir "C:\Video\subfolders\*" /AD-H /B /S 2^>nul') do (
    echo Directory is: %%I
    echo/>"%%I\%%~nxI.mp4"
)

命令 FOR 在单独的命令进程中运行,在后台命令行中以 cmd.exe /C 启动:

dir "C:\Video\subfolders\*" /AD-H /B /S 2>nul

命令DIR输出处理STDOUT这个后台命令进程

  • 因为选项 /B 的原因是裸格式 因为选项 /S
  • 所有的名字都有完整的路径
  • 非隐藏目录因为选项 /AD-H(属性目录而不是隐藏)
  • 指定目录C:\Video\subfolders及其所有子目录。

DIR 处理 STDERR 时在 C:\Video\subfolders 的整个目录树中找不到任何目录条目时输出的错误消息通过将其重定向到设备 NUL.

来抑制

阅读有关 Using Command Redirection Operators 的 Microsoft 文章,了解 2>nul 的解释。重定向运算符 > 必须在 FOR 命令行上使用脱字符 ^ 进行转义,以便在 Windows 命令解释器处理此命令时将其解释为文字字符执行命令 FOR 之前的行,它在后台启动的单独命令进程中执行嵌入式 dir 命令行。

FOR 将输出捕获到启动命令进程的 STDOUT 并逐行处理此输出并忽略空行,默认情况下也以分号开头的行,这两个分号都不会出现在这里。

FOR 还将在 spaces/tabs 上将每一行拆分为子字符串,并将仅将第一个 space/tab 分隔的字符串分配给指定的循环变量 I.目录名可以有空格,因此 delims= 用于定义一个空的字符串定界符列表,导致分配给循环变量 I 始终是当前从捕获的 [=92] 处理的完整完整目录名=]DIR 输出.

没有路径的目录名称用%%~nxI引用,这是最后一个反斜杠后的字符串。目录名也可以包含一个点,如 folder.name 或以一个点开头,如 .FolderName,尽管两者在 Windows 上都不常见。出于这个原因,有必要使用 %%~nxI(名称和扩展名)而不仅仅是 %%~nI 因为 FOR 不验证是否使用 [=33= 引用的字符串] 是文件名或目录名。

%%~nI 引用字符串 between last \(或 / 与 Linux 路径兼容,首先替换为\) 如果根本没有任何反斜杠(或斜杠) 字符串开头 如果没有 \(或 / 最后一个点,如果有 . 已经确定名称字符串的开头 字符串结尾,如果没有点。 %%~xI 引用以最后一个目录分隔符后找到的最后一个点开头的字符串。因此,分配给循环变量 I 的字符串不能是真正存在的 file/folder 的 file/folder 名称,尽管这里是这种情况。如果 I 包含以 \(或 /)结尾的字符串,%%~nxI 将是一个空字符串。

不要将可以使用循环变量引用的字符串分配给环境变量,因为这将需要使用 delayed expansion,如命令 的帮助所述SET 在命令提示符 运行 上输出 window set /?.

要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。

  • dir /?
  • echo /?
  • for /?
  • popd /?
  • pushd /?

另请参阅 DosTips 论坛主题 ECHO. FAILS to give text or blank line - Instead use ECHO/

有问题的第二个批处理文件代码不起作用,因为没有使用延迟扩展或避免在命令块中使用环境变量 defined/modified 并在同一命令块中使用语法 %variable% 进行引用. Windows 命令处理器 cmd.exe 在执行外部 FOR 第一次。请参阅 How does the Windows Command Interpreter (CMD.EXE) parse scripts? 此命令块中的所有 %variable% 引用在此预处理阶段已被引用环境变量的当前值替换。因此最终在迭代外部 FOR 上执行的代码不再包含任何 %variable% 环境变量引用,因为它可以在执行之前在 cmd.exe 输出的命令行中看到命令行。

虽然 解决了 OP 的问题,但实际上并没有回答最初提出的问题。

%CD%FOR 循环中不起作用的原因是您需要启用“delayed expansion

例如:

SETLOCAL EnableDelayedExpansion
FOR %%i in (foo bar) DO (
    pushd %%i
    echo CD with delayed expansion: !CD!
    echo CD without it: %CD%
)

假设当您 运行 时“foo”和“bar”目录存在,您应该看到 !CD! 给出了正确的路径。

更多示例:Example of delayed expansion in batch file