La respuesta seleccionada funciona, pero podría mejorarse un poco.
- Las opciones probablemente deberían inicializarse a los valores predeterminados.
- Sería bueno conservar% 0 así como los argumentos obligatorios% 1 y% 2.
- Se convierte en una molestia tener un bloque IF para cada opción, especialmente a medida que aumenta la cantidad de opciones.
- Sería bueno tener una forma simple y concisa de definir rápidamente todas las opciones y valores predeterminados en un solo lugar.
- Sería bueno admitir opciones independientes que sirvan como indicadores (sin valor después de la opción).
- No sabemos si un argumento está entre comillas. Tampoco sabemos si se pasó un valor arg utilizando caracteres de escape. Es mejor acceder a un argumento usando% ~ 1 y encerrar la asignación entre comillas. Entonces, el lote puede confiar en la ausencia de comillas adjuntas, pero los caracteres especiales siguen siendo generalmente seguros sin escaparse. (Esto no es a prueba de balas, pero maneja la mayoría de situaciones)
Mi solución se basa en la creación de una variable OPTIONS que define todas las opciones y sus valores predeterminados. OPTIONS también se utiliza para probar si una opción proporcionada es válida. Se guarda una enorme cantidad de código simplemente almacenando los valores de las opciones en variables con el mismo nombre que la opción. La cantidad de código es constante independientemente de cuántas opciones se definan; solo tiene que cambiar la definición de OPCIONES.
EDITAR - Además, el código de bucle: debe cambiar si cambia el número de argumentos posicionales obligatorios. Por ejemplo, muchas veces se nombran todos los argumentos, en cuyo caso desea analizar los argumentos que comienzan en la posición 1 en lugar de 3. Por lo tanto, dentro del ciclo:, los 3 se convierten en 1 y el 4 en 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!
Realmente no hay tanto código. La mayor parte del código anterior son comentarios. Aquí está exactamente el mismo código, sin los comentarios.
@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!
Esta solución proporciona argumentos de estilo Unix dentro de un lote de Windows. Esta no es la norma para Windows: por lo general, el lote tiene las opciones que preceden a los argumentos requeridos y las opciones tienen el prefijo /
.
Las técnicas utilizadas en esta solución se adaptan fácilmente al estilo de opciones de Windows.
- El ciclo de análisis siempre busca una opción en
%1
, y continúa hasta que arg 1 no comienza con/
- Tenga en cuenta que las asignaciones SET deben estar entre comillas si el nombre comienza con
/
.
SET /VAR=VALUE
falla
SET "/VAR=VALUE"
funciona. De todos modos, ya estoy haciendo esto en mi solución.
- El estilo estándar de Windows excluye la posibilidad de que el primer valor de argumento requerido comience con
/
. Esta limitación se puede eliminar empleando una //
opción definida implícitamente que sirve como señal para salir del ciclo de análisis de opciones. No se almacenará nada para la //
"opción".
Actualización 2015-12-28: Soporte para !
valores de opción in
En el código anterior, cada argumento se expande mientras la expansión retardada está habilitada, lo que significa que !
lo más probable es que se eliminen, o que !var!
se expanda algo similar . Además, ^
también se puede quitar si !
está presente. La siguiente pequeña modificación al código sin comentarios elimina la limitación de modo que !
y ^
se conservan en los valores de las opciones.
@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!