Выбранный ответ работает, но его можно улучшить.
- Параметры, вероятно, следует инициализировать значениями по умолчанию.
- Было бы неплохо сохранить% 0, а также необходимые аргументы% 1 и% 2.
- Становится больно иметь блок IF для каждого варианта, особенно когда количество вариантов растет.
- Было бы неплохо иметь простой и лаконичный способ быстрого определения всех параметров и значений по умолчанию в одном месте.
- Было бы хорошо поддерживать автономные параметры, которые служат в качестве флагов (без значения после параметра).
- Мы не знаем, заключен ли аргумент в кавычки. Мы также не знаем, было ли передано значение arg с использованием экранированных символов. Лучше получить доступ к аргументу, используя% ~ 1, и заключить присвоение в кавычки. Тогда пакет может полагаться на отсутствие заключительных кавычек, но специальные символы, как правило, безопасны без экранирования. (Это не пуленепробиваемое устройство, но оно подходит для большинства ситуаций)
Мое решение основано на создании переменной OPTIONS, которая определяет все параметры и их значения по умолчанию. OPTIONS также используется для проверки допустимости предоставленной опции. Огромный объем кода сохраняется, просто сохраняя значения параметров в переменных, названных так же, как параметр. Объем кода постоянен независимо от того, сколько опций определено; только определение OPTIONS должно измениться.
РЕДАКТИРОВАТЬ - Кроме того, код цикла: должен измениться, если количество обязательных позиционных аргументов изменится. Например, часто все аргументы имеют имена, и в этом случае вы хотите проанализировать аргументы, начинающиеся с позиции 1, а не 3. Итак, в цикле: все 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!
На самом деле кода не так уж и много. Большая часть приведенного выше кода - это комментарии. Вот тот же код без комментариев.
@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!
Это решение предоставляет аргументы стиля Unix в пакете Windows. Это не является нормой для Windows - пакетная программа обычно имеет параметры, предшествующие требуемым аргументам, а параметры имеют префикс /
.
Методы, используемые в этом решении, легко адаптируются к стилю опций Windows.
- Цикл синтаксического анализа всегда ищет параметр в
%1
и продолжается до тех пор, пока аргумент 1 не начнется с/
- Обратите внимание, что назначения SET должны быть заключены в кавычки, если имя начинается с
/
.
SET /VAR=VALUE
не
SET "/VAR=VALUE"
работает. В любом случае я уже делаю это в своем решении.
- Стандартный стиль Windows исключает возможность того, что первое обязательное значение аргумента может начинаться с
/
. Это ограничение может быть устранено путем использования неявно определенной //
опции, которая служит сигналом для выхода из цикла анализа опции. Для //
"опции" ничего не будет сохранено .
Обновление 2015-12-28: поддержка !
значений параметров in
В приведенном выше коде каждый аргумент раскрывается при включенном отложенном раскрытии, что означает, что !
они, скорее всего, удаляются или !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!