【问题标题】:Loop through subfolders of subfolders and execute a command on each file of the last subfolder遍历子文件夹的子文件夹并对最后一个子文件夹的每个文件执行命令
【发布时间】:2016-08-16 06:44:04
【问题描述】:

考虑这个层次结构:

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 连接到它,它可能会起作用。

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

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

【问题讨论】:

  • 对于您的任务,我将使用两个嵌套的for /D 循环来迭代主文件夹中的两个目录级别,然后使用标准的for 循环来迭代*.mkv 文件...您在单个代码块中编写和读取相同变量时需要delayed expansion 的最后一个脚本...
  • @aschipfl 你能分享代码吗,因为我不知道批处理编程。我所做的只是谷歌搜索和混合匹配的语法!!!!!!

标签: batch-file cmd batch-processing nested-loops


【解决方案1】:

您的上一个批处理代码失败是因为

  1. 引用 loop 变量 A 就像 environment 变量用 %A% 而不是 %%A
  2. 在使用() 定义的命令块中引用环境 变量定义/设置需要使用延迟扩展 之前通过命令行@987654328 启用@ 并使用!sourcedir!!outputdir! 而不是%sourcedir%%outputdir%,它们已经被环境 变量sourcediroutputdir 的当前值替换(此处为空字符串)之前定义)当 Windows 命令处理器在第一次执行命令 FOR 之前解析整个命令块时。
  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"
        )
    )
)

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

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

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

【讨论】:

  • 感谢您的回答,您应该可以正常工作。该死的,你的回答太棒了,你能告诉我在哪里可以学习 Windows Shell 编程或者像你这样的东西吗?考虑到我已经创建了 Main 和 Converted 文件夹,我运行了一个更简单的代码,我将在下面给出。再次感谢伙计。
  • 我很确定所有批处理脚本编写专家都是从阅读(命令帮助、文档、www 中的文章)开始的,然后尝试,阅读更多,尝试更多,等等,直到获得经验在批处理脚本编写中。然而,几乎总是必须测试批处理脚本的每一行,因为语法检查发生在脚本执行时,因为没有像 C/C++/C# 和其他编程语言那样的预处理和编译步骤。 SS64的An A-Z Index of the Windows CMD command line对初学者很有帮助。
【解决方案2】:

@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"
)

【讨论】:

  • 第二个 FOR 循环中的 %%A\Main\*.mkv 应用双引号引起来,否则批处理文件将在包含 1 个或多个空格或其他关键字符(如 @)的文件夹路径上失败987654323@ 在路径中。
猜你喜欢
  • 2014-05-11
  • 1970-01-01
  • 2017-02-08
  • 2018-12-20
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
  • 2014-09-28
相关资源
最近更新 更多