如何将批处理文件的参数转换为JSON?
我有一个.bat脚本,带着几个参数,最后一个参数 -plusthis(可选,但必须是最后一个)后面跟着用双引号括起来、键值对形式的参数,键和值之间用等号分隔:
.\stuff.bat -oz -a yes -b no ... -plusthis "param1=dix" "par2=onze"
我想把这两对(可能还有更多……)提取并转换成某种类似JSON的集合,形成一个字符串:
"param1":"dix","par2":"onze"
如何进行这段简单的处理?
解决方案
下面是另一种对批处理文件 stuff.cmd 进行执行时可用的解决方案,以及对应的命令行:
stuff.cmd -oz -a yes -b no ... -plusthis "param1=dix" "par2=onze" NoValue "Empty Value=";"Hello=World!","SpecialChars=&<=>$" "Test=Is that 98% or 100%"
输出结果是:
The JSON data: "param1":"dix","par2":"onze","NoValue":"","Empty Value":"","Hello":"World!","SpecialChars":"&<=>$","Test":"Is that 98% or 100%"
这个标注为注释的批处理文件 stuff.cmd 是:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Get all arguments passed to the batch file assigned as one string to
rem an environment variable named Arguments. That arguments string cannot
rem have more than 8181 characters (8192 characters with variable name
rem Arguments (nine characters), the equal sign, and the terminating null).
set Arguments=%*
rem Enable now delayed variable expansion and check if the batch file was
rem called with at least one argument. There could be also used a GOTO to
rem a label which outputs the usage help for the batch file instead of the
rem output of an error message and batch processing exit due to an error
rem detected on arguments parsing as shown below with commented command line.
rem if not defined Arguments goto OutputHelp
setlocal EnableDelayedExpansion
if not defined Arguments echo No arguments passed to: %~n0& goto ArgError
rem Verify if the option -plusthis is present in the arguments list.
rem There could be used also the following commented command line if the
rem missing option should not be interpreted as an arguments error.
rem if "!Arguments!" == "!Arguments:-plusthis=!" endlocal & goto EndBatch
if "!Arguments!" == "!Arguments:-plusthis=!" echo No option -plusthis in arguments list.& goto ArgError
rem Remove everything up to end of the option -plusthis (case-insensitive).
set Arguments=!Arguments:*-plusthis=!
rem Does the list of arguments end with the argument -plusthis without
rem no more arguments like the expected key and value pairs?
if not defined Arguments echo No arguments after option: -plusthis& goto ArgError
rem Restore former environment with redefinition of variable Arguments
rem in that environment with the string value in the current environment.
endlocal & set Arguments=%Arguments%
rem Process the remaining list of arguments.
set "DataJSON="
(for %%G in (%Arguments%) do for /F "tokens=1* delims== eol=" %%H in ("%%~G") do set "Key=%%~H" & set "Value=%%~I" & call :AppendData) & goto FinishData
:AppendData
set DataJSON=%DataJSON%,"%Key%":"%Value%"& goto :EOF
:OutputHelp
echo Usage help for %~ne0:
goto EndBatch
:FinishData
set DataJSON=%DataJSON:~1%
echo The JSON data: %DataJSON%
goto EndBatch
:ArgError
endlocal
:EndBatch
endlocal
以 REMARK 命令开头的行 rem 是注释行,可以删除。两行 注释行 是 命令行,展示了另一种 IF 条件处理方式。请阅读注释,选择更适合你批处理文件的 IF 命令行。
传入字符串中任意位置出现的 * 或 ? 的键值对都不会被该批处理文件正确处理。可以通过以下命令行运行批处理文件来看到这一点:
stuff.cmd -oz -a yes -b no ... -plusthis "param1=dix" "par2=onze" NoValue "Empty Value=" "Hello=World!" "SpecialChars=&<=>$*" "Test=Is that 98% or 100%?"
请注意在 SpecialChars 的值中包含 *,以及在最后一个参数字符串的末尾包含 ?。
在这种情况下的输出是:
The JSON data: "param1":"dix","par2":"onze","NoValue":"","Empty Value":"","Hello":"World!"
输出中完全缺失了 SpecialChars 和 Test 这两个键。
原因: for %%G in (%Arguments%) do 将包含 * 或 ? 的参数字符串视为通配符模式,并在当前目录中搜索名称与该通配符模式匹配的非隐藏文件。若找到一个或多个匹配的文件名,FOR 循环会把一个匹配的文件名依次赋给循环变量 I,并对每个文件名执行命令。当参数字符串中包含 * 或 ? 时,在没有找到任何与通配符模式匹配的非隐藏文件名时,该参数字符串将被忽略。
还需要更加强化的代码,才能正确处理包含 * 或 ? 的参数字符串,这些应被解释为字面字符而非通配符。
一个以一个或多個 = 开头的值,也不会被该批处理文件正确解释,如下运行所示:
stuff.cmd -plusthis "key===value"
此示例的输出是:
The JSON data: "key":"value"
在这种情况下,紧挨着 value 的两个等号被内部的 FOR 删除。
原因: for /F "tokens=1* delims== eol=" %%H in ("%%~G") do 将一串等号视为定界符,而不仅仅是第一个。
要理解所使用的命令及其工作原理,请打开一个 命令提示符 窗口,在其中执行下列命令,并仔细完整地阅读每个命令所显示的帮助页面。
call /?echo /?endlocal /?for /?goto /?if /?rem /?set /?setlocal /?
另请参见:
- How does the Windows Command Interpreter (CMD.EXE) parse scripts?
- This answer 了解SETLOCAL与 ENDLOCAL命令的细节。
- Single line with multiple commands using Windows batch file,解释在批处理文件中多次使用的无条件命令运算符
&。
注: 命令 echo 输出的信息与无条件命令运算符 & 之间,在下一个命令 goto 之前没有空格;若有空格,echo 也会将其输出为尾随空格。& 与下一个命令 goto 之间的空格,由处理批处理文件的 cmd.exe 将其解释为参数分隔符。
以下是上述内容的一个增强版本 stuff.cmd,只要数据参数中从不出现 #$QM$#,它也能正确处理其中的 ?。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Get all arguments passed to the batch file assigned as one string to
rem an environment variable named Arguments. That arguments string cannot
rem have more than 8181 characters (8192 characters with variable name
rem Arguments (nine characters), the equal sign, and the terminating null).
set Arguments=%*
rem Enable now delayed variable expansion and check if the batch file was
rem called with at least one argument. There could be also used a GOTO to
rem a label which outputs the usage help for the batch file instead of the
rem output of an error message and batch processing exit due to an error
rem detected on arguments parsing as shown below with commented command line.
rem if not defined Arguments goto OutputHelp
setlocal EnableDelayedExpansion
if not defined Arguments echo No arguments passed to: %~n0& goto ArgError
rem Verify if the option -plusthis is present in the arguments list.
rem There could be used also the following commented command line if the
rem missing option should not be interpreted as an arguments error.
rem if "!Arguments!" == "!Arguments:-plusthis=!" endlocal & goto EndBatch
if "!Arguments!" == "!Arguments:-plusthis=!" echo No option -plusthis in arguments list.& goto ArgError
rem Remove everything up to end of the option -plusthis (case-insensitive).
set Arguments=!Arguments:*-plusthis=!
rem Does the list of arguments end with the argument -plusthis without
rem no more arguments like the expected key and value pairs?
if not defined Arguments echo No arguments after option: -plusthis& goto ArgError
rem Replace the character ? by the string: #$QM$#
set Arguments=!Arguments:?=#$QM$#!
rem Restore former environment with redefinition of variable Arguments
rem in that environment with the string value in the current environment.
endlocal & set Arguments=%Arguments%
rem Process the remaining list of arguments.
set "DataJSON="
(for %%G in (%Arguments%) do for /F "tokens=1* delims== eol=" %%H in ("%%~G") do set "Key=%%~H" & set "Value=%%~I" & call :AppendData) & goto FinishData
:AppendData
set DataJSON=%DataJSON%,"%Key%":"%Value%"& goto :EOF
:OutputHelp
echo Usage help for %~ne0:
goto EndBatch
:FinishData
set DataJSON=%DataJSON:~1%
rem Replace the string #$QM$# by the character: ?
set DataJSON=%DataJSON:#$QM$#=?%
echo The JSON data: %DataJSON%
goto EndBatch
:ArgError
endlocal
:EndBatch
endlocal
但对于键值对参数中任意位置出现的 *,以及值开头出现一个或多个 = 的问题,仍然不能通过该增强版批处理脚本解决。
见: