所选答案有效,但可能需要一些改进。
- 这些选项可能应该初始化为默认值。
- 最好保留 %0 以及所需的参数 %1 和 %2。
- 为每个选项设置一个 IF 块变得很痛苦,尤其是随着选项数量的增加。
- 如果有一种简单明了的方法可以在一个地方快速定义所有选项和默认值,那就太好了。
- 最好支持用作标志的独立选项(选项后面没有值)。
- 我们不知道 arg 是否包含在引号中。我们也不知道是否使用转义字符传递了 arg 值。最好使用 %~1 访问 arg 并将赋值用引号括起来。然后批处理可以依靠没有括起来的引号,但是特殊字符通常仍然是安全的而无需转义。 (这不是防弹的,但它可以处理大多数情况)
我的解决方案依赖于创建一个定义所有选项及其默认值的 OPTIONS 变量。 OPTIONS 还用于测试提供的选项是否有效。通过简单地将选项值存储在与选项名称相同的变量中,可以节省大量代码。无论定义了多少选项,代码量都是恒定的;只有 OPTIONS 定义需要改变。
编辑 - 此外,如果强制位置参数的数量发生变化,则 :loop 代码必须更改。例如,通常所有参数都被命名,在这种情况下,您希望从位置 1 而不是 3 开始解析参数。因此在 :loop 中,所有 3 都变为 1,而 4 变为 2。
@echo off
setlocal enableDelayedExpansion
:: Define the option names along with default values, using a <space>
:: delimiter between options. I'm using some generic option names, but
:: normally each option would have a meaningful name.
::
:: Each option has the format -name:[default]
::
:: The option names are NOT case sensitive.
::
:: Options that have a default value expect the subsequent command line
:: argument to contain the value. If the option is not provided then the
:: option is set to the default. If the default contains spaces, contains
:: special characters, or starts with a colon, then it should be enclosed
:: within double quotes. The default can be undefined by specifying the
:: default as empty quotes "".
:: NOTE - defaults cannot contain * or ? with this solution.
::
:: Options that are specified without any default value are simply flags
:: that are either defined or undefined. All flags start out undefined by
:: default and become defined if the option is supplied.
::
:: The order of the definitions is not important.
::
set "options=-username:/ -option2:"" -option3:"three word default" -flag1: -flag2:"
:: Set the default option values
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop
:: Validate and store the options, one at a time, using a loop.
:: Options start at arg 3 in this example. Each SHIFT is done starting at
:: the first option so required args are preserved.
::
if not "%~3"=="" (
set "test=!options:*%~3:=! "
if "!test!"=="!options! " (
rem No substitution was made so this is an invalid option.
rem Error handling goes here.
rem I will simply echo an error message.
echo Error: Invalid option %~3
) else if "!test:~0,1!"==" " (
rem Set the flag option using the option name.
rem The value doesn't matter, it just needs to be defined.
set "%~3=1"
) else (
rem Set the option value using the option as the name.
rem and the next arg as the value
set "%~3=%~4"
shift /3
)
shift /3
goto :loop
)
:: Now all supplied options are stored in variables whose names are the
:: option names. Missing options have the default value, or are undefined if
:: there is no default.
:: The required args are still available in %1 and %2 (and %0 is also preserved)
:: For this example I will simply echo all the option values,
:: assuming any variable starting with - is an option.
::
set -
:: To get the value of a single parameter, just remember to include the `-`
echo The value of -username is: !-username!
确实没有那么多代码。上面的大部分代码都是cmets。这是完全相同的代码,但没有 cmets。
@echo off
setlocal enableDelayedExpansion
set "options=-username:/ -option2:"" -option3:"three word default" -flag1: -flag2:"
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop
if not "%~3"=="" (
set "test=!options:*%~3:=! "
if "!test!"=="!options! " (
echo Error: Invalid option %~3
) else if "!test:~0,1!"==" " (
set "%~3=1"
) else (
set "%~3=%~4"
shift /3
)
shift /3
goto :loop
)
set -
:: To get the value of a single parameter, just remember to include the `-`
echo The value of -username is: !-username!
此解决方案在 Windows 批处理中提供 Unix 样式参数。这不是 Windows 的规范 - 批处理通常在所需参数之前具有选项,并且选项以 / 为前缀。
此解决方案中使用的技术很容易适应 Windows 样式的选项。
- 解析循环总是在
%1 处寻找一个选项,并一直持续到arg 1 不以/ 开头
- 请注意,如果名称以
/ 开头,则 SET 分配必须括在引号内。
SET /VAR=VALUE 失败
SET "/VAR=VALUE" 有效。无论如何,我已经在我的解决方案中这样做了。
- 标准 Windows 样式排除了第一个必需参数值以
/ 开头的可能性。可以通过使用隐式定义的// 选项来消除此限制,该选项用作退出选项解析循环的信号。不会为//“选项”存储任何内容。
2015-12-28 更新:在选项值中支持 !
在上面的代码中,每个参数都在启用延迟扩展的情况下进行扩展,这意味着! 很可能被剥离,或者!var! 之类的东西被扩展。此外,如果存在!,也可以剥离^。以下对未注释代码的小修改消除了限制,以便将! 和^ 保留在选项值中。
@echo off
setlocal enableDelayedExpansion
set "options=-username:/ -option2:"" -option3:"three word default" -flag1: -flag2:"
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop
if not "%~3"=="" (
set "test=!options:*%~3:=! "
if "!test!"=="!options! " (
echo Error: Invalid option %~3
) else if "!test:~0,1!"==" " (
set "%~3=1"
) else (
setlocal disableDelayedExpansion
set "val=%~4"
call :escapeVal
setlocal enableDelayedExpansion
for /f delims^=^ eol^= %%A in ("!val!") do endlocal&endlocal&set "%~3=%%A" !
shift /3
)
shift /3
goto :loop
)
goto :endArgs
:escapeVal
set "val=%val:^=^^%"
set "val=%val:!=^!%"
exit /b
:endArgs
set -
:: To get the value of a single parameter, just remember to include the `-`
echo The value of -username is: !-username!