Windows 批处理 - 为 ffmpeg 处理提取值

Windows Batch - Extract values for ffmpeg processing

我有几十个 json 文件,我试图在每个文件中找到两个值,并将结果分配给两个单独的变量以进行 ffmpeg 处理。

示例 json 文件如下所示:

{
    "year": "2018",
    "track": "12",
... other data omitted
}

我希望提取 2018 和 12,以便我可以在以下 ffmpeg 命令中使用它们:

ffmpeg -i "same_file_name_as_json.m4a" -metadata:s:a:0 year=2018 --metadata:s:a:0 track=12 -acodec libmp3lame "same_file_name_as_json.mp3"

是否可以编写一个批处理文件来达到预期的效果?任何帮助将不胜感激,因为我是 findstr 和设置变量的完全新手。谢谢。

已编辑:

set "year=" & set "track="
for %%i in (*.json) do (
  for /f "usebackq tokens=1,2 delims={:}, " %%a in ("%%i") do (
    set "%%~a=%%~b"
    if defined year if defined track goto :CONT
    )
:CONT
C:\ffmpeg -i "%%~ni.m4a" -metadata:s:a:0 year=%year% -metadata:s:a:0 track=%track% -acodec libmp3lame "%%~ni.mp3"
)
pause

Windows 批处理脚本不理解 JSON 文件格式,因此最好使用原生支持它的语言。将 JSON 视为“普通”文本并不是最好的主意,因为只有轻微的更改(例如,添加、删除或移动 line-breaks)才不会违反 JSON格式还是会出大问题的

就是说,假设 JSON 文件与您显示的完全一样,并且它具有 Unix- 或 DOS/Windows-style line-breaks(即 carriage-return字符后跟 line-feed 字符),此代码可能适合您:

for /F "usebackq tokens=1,2 delims={:}, " %%M in ("file.json") do set "%%~M=%%~N"
echo year  = %year%
echo track = %track%

如果您有一个巨大的 JSON 文件,您不想对其进行不必要的完全处理,您可以改用此代码:

set "year=" & set "track="
for /F "usebackq tokens=1,2 delims={:}, " %%M in ("file.json") do (
    set "%%~M=%%~N"
    if defined year if defined track goto :CONT
)
:CONT
echo year  = %year%
echo track = %track%

如果您要提取的 (non-array) 值也可能包含定义的分隔符之一 ({:},, SPACE), 如果值不包含字符 *, ?, <, >:

set "year=" & set "track="
for /F "usebackq tokens=1,* delims={:}, " %%M in ("file.json") do (
    for %%K in (%%N) do set "%%~M=%%~K"
    if defined year if defined track goto :CONT
)
:CONT
echo year  = %year%
echo track = %track%

为了防止脚本分配不需要的多余变量,您可以试试这个:

for /F "usebackq tokens=1,2 delims={:}, " %%M in ("file.json") do (
    if "%%~M"=="year" (set "%%~M=%%~N") else if "%%~M"=="track" set "%%~M=%%~N"
)
echo year  = %year%
echo track = %track%

或者这个,它通过findstr命令预处理数据并过滤掉所需的行:

for /F "tokens=1,2 delims={:}, " %%M in ('
    findstr /R /C:"^ *\"year\" *:" /C:"^ *\"track\" *:" "file.json"
') do set "%%~M=%%~N"
echo year  = %year%
echo track = %track%

基于您的edit, let me suggest to use the last of the above methods, because there is no goto :CONT, which cannot be used within loops as it breaks the block context, and it does not assign additional unwanted variables. Since variables are written and read within the loop body, you have to enable and apply delayed variable expansion。我会按照以下方式进行所有操作:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* Iterate over the `*.json` files in the current working directory (`%CD%`);
rem    to use the parent directory of this script, use `%~dp0*.json` instead: */
for %%I in ("*.json") do (
    rem // Store name of current JSON file in variable:
    set "name=%%~nI"
    rem // Clear variables for later check for availability:
    set "year=" & set "track="
    rem // Process the current JSON file:
    for /F "tokens=1,2 delims={:}, " %%M in ('
        findstr /R /C:"^ *\"year\" *:" /C:"^ *\"track\" *:" "%%~I"
    ') do (
        rem // Assign year and track variables:
        set "%%~M=%%~N"
        rem // Check of both year and track are available:
        if defined year if defined track (
            rem // Toggle delayed expansion to avoid troubles with `!`:
            setlocal EnableDelayedExpansion
            rem // Eventually execute `ffmpeg` tool using all the derived data:
            ffmpeg -i "!name!.m4a" -metadata:s:a:0 year=!year! -metadata:s:a:0 track=!track! -acodec libmp3lame "!name!.mp3"
            endlocal
        )
    )
)
endlocal
exit /B

I have dozens of json files...

Windows' cmd 不支持 JSON,因此您必须求助于 PowerShell,或使用支持的外部工具。您可能会发现 很有趣。

要提取“year”和“track”的值:

xidel -s input.json -e "$json/(year,track)"
#or
xidel -s input.json -e "$json/year,$json/track"
2018
12

导出到变量 %year%%track%:

FOR /F "delims=" %A IN ('xidel -s input.json -e "$json/(year:=year,track:=track)" --output-format^=cmd') DO %A
#or
FOR /F "delims=" %A IN ('xidel -s input.json -e "year:=$json/year,track:=$json/track" --output-format^=cmd') DO %A

然而,您不需要变量来创建您想要的字符串(ffmpeg 命令)。 xidel 也可以做到。

您可以使用 FOR 循环遍历所有 JSON-files...

FOR %A IN (*.json) DO @xidel -s %A -e "$json/concat('ffmpeg -i \"%~nA.m4a\" -metadata:s:a:0 year=',year,' --metadata:s:a:0 track=',track,' -acodec libmp3lame \"%~nA.mp3\"')"
ffmpeg -i "name-of-json-file.m4a" -metadata:s:a:0 year=2018 --metadata:s:a:0 track=12 -acodec libmp3lame "name-of-json-file.mp3"

...但是为每个 JSON-file 调用 xidel 是非常低效的。 xidel 可以更有效地做到这一点。
xidel 等价于 FOR %A IN (*.json) DO @ECHO %Axidel -se "file:list(.,false(),'*.json')"

然后您可以使用以下查询一次处理所有 JSON-files:

xidel -se "for $x in file:list(.,false(),'*.json') return json-doc($x)/concat('ffmpeg -i \"',replace($x,'json','m4a'),'\" -metadata:s:a:0 year=',year,' --metadata:s:a:0 track=',track,' -acodec libmp3lame \"',replace($x,'json','mp3'),'\"')"

美化command/query:

xidel -se ^"^
  for $x in file:list(.,false(),'*.json') return^
  json-doc($x)/concat(^
    'ffmpeg -i \^"',^
    replace($x,'json','m4a'),^
    '\^" -metadata:s:a:0 year=',^
    year,^
    ' --metadata:s:a:0 track=',^
    track,^
    ' -acodec libmp3lame \^"',^
    replace($x,'json','mp3'),^
    '\^"'^
  )^
"