带有一组处理命令行输出的for /F 命令导致在后台启动另一个带有%SystemRoot%\System32\cmd.exe /c 的Windows 命令处理器,并且命令行作为附加参数附加。因此,对于在后台运行的此命令进程,不启用延迟变量扩展,因为 echo !mkvmergeinfo! 需要这样做。有必要使用选项/V:ON left 到选项/c 和要执行的命令行运行后台cmd.exe。但这是不可能的,除了运行两个额外的cmd.exe,第一个以/c 和一个以%ComSpec% /D /V:ON /C ... 开头的命令行,第二个以cmd.exe 开头,正如aschipfl 在上面的评论中所建议的那样。
我建议为每个*.mkv 文件运行一次mkvmerge.exe -i 并仅使用cmd.exe 的内部命令(如FOR 和IF 不使用@ 987654338@ 和 findstr.exe。
例子:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
for %%G in (*.mkv) do (
echo/
echo Processing file "%%G" ...
for /F "delims=" %%H in ('mkvmerge.exe -i "%%G"') do (
echo/
echo Processing info line: %%H
for /F "tokens=1,3,4* delims=:( " %%I in ("%%H") do (
if "%%I" == "Track" (
for /F "delims=)" %%M in ("%%L") do (
if "%%K" == "video" (
echo ... Track %%J is a video track with video codec %%M
) else if "%%K" == "audio" (
echo ... Track %%J is an audio track with audio codec %%M
) else if "%%K" == "subtitles" (
echo ... Track %%J is a subtitles track with subtitles codec %%M
)
)
) else if "%%I" == "Attachment" (
echo ... Attachment %%J is %%K %%L
)
)
)
)
endlocal
如何进一步处理mkvmerge.exe -i "%%G" 输出的每一行取决于您。这只是一个演示,它会导致文件test.mkv 的发布信息的以下输出。
Processing file "test.mkv" ...
Processing info line: File 'test.mkv': container: Matroska
Processing info line: Track ID 0: video (AVC/H.264/MPEG-4p10)
... Track 0 is a video track with video codec AVC/H.264/MPEG-4p10
Processing info line: Track ID 1: audio (Opus)
... Track 1 is an audio track with audio codec Opus
Processing info line: Track ID 2: subtitles (SubRip/SRT)
... Track 2 is a subtitles track with subtitles codec SubRip/SRT
Processing info line: Attachment ID 1: type 'image/jpeg', size 30184 bytes, file name 'test.jpg'
... Attachment 1 is type 'image/jpeg', size 30184 bytes, file name 'test.jpg'
Processing info line: Attachment ID 2: type 'image/jpeg', size 30184 bytes, file name 'test2.jpg'
... Attachment 2 is type 'image/jpeg', size 30184 bytes, file name 'test2.jpg'
Processing info line: Attachment ID 3: type 'image/jpeg', size 30184 bytes, file name 'test3.jpg'
... Attachment 3 is type 'image/jpeg', size 30184 bytes, file name 'test3.jpg'
Processing info line: Chapters: 9 entries
所以第一行和最后一行信息输出并没有真正处理,而对第一个子字符串(令牌)区分大小写的行进行进一步处理Track或Attachment。
第二个 FOR 命令行导致在后台启动,Windows 安装到 C:\Windows 另一个 Windows 命令处理器,当前文件的以下命令行为 test.mkv:
C:\Windows\System32\cmd.exe /c mkvmerge.exe -i "test.mkv"
Windows 命令处理器必须首先使用环境变量PATHEXT 和PATH 搜索文件mkvmerge.exe。因此,如果批处理文件必须处理 100 个.mkv 文件,则很可能总共需要执行数千次文件系统访问才能一次又一次地找到该可执行文件。使用完全限定的文件名,即 mkvmerge.exe 及其完整路径,将有助于将文件系统访问次数减少到恰好 100 次 .mkv 文件的 100 次 mkvmerge.exe 执行次数。
一旦找到mkvmerge.exe,在后台启动的第二个cmd.exe 会使用其完整路径调用此可执行文件并将两个参数传递给它。 mkvmerge.exe输出数据处理后台命令进程的STDOUT。
处理后台命令进程的STDOUT的输出由cmd.exe处理批处理文件捕获,FOR在启动@987654360后逐行处理@在mkvmerge.exe完成后自行关闭。
FOR 带有选项/F 总是忽略空行。这里没问题。
默认情况下,非空行将被拆分为子字符串(标记),使用普通空格和水平制表符作为字符串分隔符,然后将检查第一个子字符串以分号开头,这是默认的行尾字符,其中如果该行也将被忽略以进行进一步处理,最后将第一个空格/制表符分隔的子字符串分配给指定的循环变量H。但是在这种情况下不需要默认的行处理行为。出于这个原因,delims= 用于定义一个空的分隔符列表,这导致将整个捕获的行分配给循环变量H,除了该行将以; 开头,因为@987654367 可以在此处排除@ 不输出以分号开头的行。
演示代码将整行输出到控制台窗口,以便进一步处理该行的样子。
第二个for /F 循环现在是处理行数据的真正工作。分配给循环变量 H 的行在圆括号内的双引号中指定,导致 FOR 将该行解释为字符串以像从后台执行的命令进程中捕获的行或读取的行一样处理来自文本文件。
这次字符串分隔符是用冒号、左圆括号和使用delims=:( 的普通空格来定义的。还用tokens=1,3,4* 指定了感兴趣的不仅是第一个子字符串,而且第一、第三、第四和该行的其余部分没有使用分隔符进一步拆分为子字符串。
例如,让我们看看现在发生了什么:
Track ID 0: video (AVC/H.264/MPEG-4p10)
这一行被分成:
-
Track 被分配给指定的循环变量I。
-
ID 根本没有进一步处理。
-
0 根据ASCII table 分配给下一个循环变量J。
这就是循环变量区分大小写的原因。
-
video 根据 ASCII 表分配给循环变量 K。
-
AVC/H.264/MPEG-4p10) 这是分配给循环变量 L 的行的其余部分。
分配给循环变量I 到L 的字符串使用IF 条件进一步处理,其中以Track 开头的行的编解码器字符串末尾的圆括号是使用带有delims=) 的另外一个for /F 命令删除。
也可以直接将捕获的行拆分为子字符串,如以下代码所示,该代码另外忽略所有以 Attachment 开头的行,而之前处理的行以 Track 开头。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
for %%G in (*.mkv) do (
set "Tracks="
echo/
echo Processing file "%%G" ...
for /F "tokens=1,3,4* delims=:( " %%H in ('mkvmerge.exe -i "%%G"') do (
if "%%H" == "Track" (
set "Tracks=1"
for /F "delims=)" %%L in ("%%K") do (
if "%%J" == "video" (
echo ... Track %%I is a video track with video codec %%L
) else if "%%J" == "audio" (
echo ... Track %%I is an audio track with audio codec %%L
) else if "%%J" == "subtitles" (
echo ... Track %%I is a subtitles track with subtitles codec %%L
)
)
) else if not defined Tracks (
if "%%H" == "Attachment" echo ... Attachment %%I is %%J %%K
)
)
)
endlocal
文件test.mkv的演示代码的输出是:
Processing file "test.mkv" ...
... Track 0 is a video track with video codec AVC/H.264/MPEG-4p10
... Track 1 is an audio track with audio codec Opus
... Track 2 is a subtitles track with subtitles codec SubRip/SRT
在处理mkvmerge.exe 输出的行之前,每个文件的环境变量Tracks 首先是未定义的set "Tracks="。如果有一行以Track 开头,则环境变量Tracks 定义为值1,因此该值无关紧要。
在所有其他行中,首先检查环境变量Tracks 是否仍未定义。如果由于之前至少处理了以Track 开头的一行而导致此条件不成立,则该行将被完全忽略。否则,之前没有处理任何跟踪信息,因此以Attachment 开头的行接下来会处理有关附件的打印信息。在任何情况下,第一行 File 和最后一行 Chapters 仍然会被完全忽略。
要了解所使用的命令及其工作原理,请打开command prompt 窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。
echo /?
endlocal /?
for /?
if /?
setlocal /?
另请参阅我在Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files 上的回答以了解为什么将两个字符串括在" 中进行比较是有意义的,而将它们括在[ 和] 中是没有意义的,因为方括号对 Windows 命令处理器没有特殊意义。