Windows批处理-提取值以进行ffmpeg处理

问题描述

我有几十个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视为“普通”文本不是最好的主意,因为只有一点不违反JSON格式的更改(例如,添加,删除或移动的换行符)仍然会造成很大的麻烦。>

也就是说,假设JSON文件完全按照您显示的样子显示,并且具有Unix或DOS / Windows风格的换行符(即回车符和换行符),该代码可以为您工作:

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," %%M in ("file.json") do (
    set "%%~M=%%~N"
    if defined year if defined track goto :CONT
)
:CONT
echo year  = %year%
echo track = %track%

如果要提取的(非数组)值也可能包含定义的定界符之一({:}, 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," %%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," %%M in ('
    findstr /R /C:"^ *\"year\" *:" /C:"^ *\"track\" *:" "file.json"
') do set "%%~M=%%~N"
echo year  = %year%
echo track = %track%

基于您的edit,让我建议使用上述方法中的最后一种,因为没有goto :CONT,因为它会破坏块上下文,因此无法在循环中使用,并且确实不分配其他不需要的变量。由于变量是在循环体内写入读取的,因此您必须启用并应用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," %%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
,

我有几十个json文件...

Windows的cmd不支持JSON,因此您必须诉诸PowerShell或使用支持此功能的外部工具。您可能会发现有趣。

要提取“年份”和“曲目”的值:

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文件...

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文件调用xidel效率很低。 xidel可以更有效地做到这一点。
xidelFOR %A IN (*.json) DO @ECHO %A等效为xidel -se "file:list(.,false(),'*.json')"

然后,您可以使用以下查询一次处理所有JSON文件:

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

预设的命令/查询:

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