遍历子文件夹的子文件夹并对最后一个子文件夹的每个文件执行命令

Loop through subfolders of subfolders and execute a command on each file of the last subfolder

考虑这个层次结构:

MainFolder\Sub_Folder1\Original_Files\
                      \Converted_Files\
          \Sub_Folder2\Original_Files\
                      \Converted_Files\

现在在每个 ...\Original_Files\ 中我有一堆视频文件,我将对其进行编码并保存到相应的 ...\Converted_Files\

我可以用这个批处理代码为一个子文件夹做这件事:

@echo off
set "sourcedir=G:\Animation\Anime\OnePiece\Episodes\Main"
set "outputdir=G:\Animation\Anime\OnePiece\Episodes\Converted"

PUSHD "%sourcedir%"

    for %%F in (*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%outputdir%\%%F"

POPD

我生成了一个文本文件,其中包含所有子文件夹的文件夹路径,其中包含:

G:\Animation\ToConvert\Berserk_1997_The_Complete_Series   
G:\Animation\ToConvert\Blue_Exorcist   
G:\Animation\ToConvert\Elfen_Lied

文件中列出的每个文件夹中都有 MainConverted 文件夹。我必须遍历 Main 中的所有文件并保存到 Converted 中,正如您在上面看到的那样。

这是我想出的:

@echo off
for /F "tokens=*" %%A in (f.txt) DO (
    set "sourcedir=%A%\Main"
    set "outputdir=%A%\Converted"
    PUSHD "%sourcedir%"
       for %%F in (*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%outputdir%\%%F"
    POPD 
) %%A

运行 for /F "tokens=*" %A in (f.txt) DO @echo %A 给我子文件夹的名称。

我想如果我可以将名称传递给某个变量并将 \Main\Converted 连接到它,它可能会起作用。

但是在 运行 在命令提示符 window 中运行上面的代码时,它只是将当前目录从我正在 运行 运行批处理文件的文件夹切换到 C:\Windows.

如何 运行 嵌套循环,一个用于子文件夹,然后在 Main 中工作和保存 Converted 之间进行选择,下一个循环用于 [=16= 中的文件]?

您最后的批处理代码因

而失败
  1. 引用 loop 变量 A 就像使用 %A% 而不是 [=14= 的 environment 变量] 和
  2. 在用 () 定义的命令块中引用 environment 变量 defined/set 需要使用 delayed expansion 在命令行 setlocal EnableDelayedExpansion 之前启用并使用 !sourcedir!!outputdir! 而不是 %sourcedir%%outputdir% 已经被当前值替换环境变量sourcediroutputdir(此处为空字符串,之前未定义)当Windows命令处理器在执行命令之前解析整个命令块对于第一次。
  3. %%A 结束后的右括号对于 Windows 命令解释器是未知的,因此由于语法错误导致批处理退出。

但是,使用以下代码比需要首先创建包含文件夹路径的文本文件的代码更好:

@echo off
for /D %%D in ("G:\Animation\ToConvert\*") do (
    if exist "%%D\Main\*.mkv" (
        echo Processing %%D ...
        if not exist "%%D\Converted\*" md "%%D\Converted"
        for %%I in ("%%D\Main\*.mkv") do (
            ffmpeg.exe -i "%%I" -s 640x480 -map 0 -c:v libx265 "%%D\Converted\%%~nxI"
        )
    )
)

带有参数 /D 的外部 FOR 仅在文件夹 G:\Animation\ToConvert 中找到非隐藏的子文件夹,并在循环变量 D 中保存完整路径不以反斜杠结尾的子文件夹。

IF 条件检查当前子文件夹中是否有一个文件夹 Main 包含 1 个或多个要处理的 *.mkv 文件。如果这个条件成立,

  • 输出信息消息以查看 运行 批处理文件的进度,
  • 在当前子文件夹中创建文件夹 Converted(如果尚不存在),
  • 执行另一个 FOR 循环以处理在当前子文件夹的文件夹 Main 中找到的每个 *.mkv 文件。

循环变量I保存当前*.mkv文件的全路径名。所以 "%%I" 可以用于输入文件,因为当前目录并不重要,因为输入文件名是完整路径。

对于输出文件,指定了当前子文件夹中的文件夹 Converted,并附加了 %%~nxI 输入文件的文件名和文件扩展名作为输出文件的名称。

此批处理代码不需要延迟扩展,因为没有使用环境变量,只有循环变量 DI.

为了完整起见,您的代码还使用一个文本文件逐行包含要处理的文件夹,并删除所有不必要的环境变量,以便 运行 批处理文件无需使用命令 setlocalendlocal.

@echo off
for /F "usebackq tokens=*" %%A in ("f.txt") do (
    if exist "%%A\Main\*.mkv" (
        echo Processing %%A ...
        if not exist "%%A\Converted\*" md "%%A\Converted"
        for %%I in ("%%A\Main\*.mkv") do (
            ffmpeg.exe -i "%%I" -s 640x480 -map 0 -c:v libx265 "%%A\Converted\%%~nxI"
        )
    )
)

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

  • echo /?
  • for /?
  • if /?
  • md /?

顺便说一句:如果您想了解什么是延迟扩展以及此处不需要什么命令 setlocalendlocal,请参阅 和链接到那里的其他答案。

@Mofi 写了一个很好的答案,他的代码都可以完美运行。这只是一个更简单的版本,我是 运行 因为该程序中检查的条件已经满足。

@echo off
for /F "tokens=*" %%A in (f.txt) DO (
        for %%F in (%%A\Main\*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%%A\Converted\%%~nxF"
)