Wybrana odpowiedź działa, ale przydałaby się pewna poprawa.
- Opcje powinny prawdopodobnie zostać zainicjalizowane na wartości domyślne.
- Byłoby dobrze zachować% 0, jak również wymagane argumenty% 1 i% 2.
- Uciążliwe staje się posiadanie bloku IF dla każdej opcji, zwłaszcza gdy rośnie liczba opcji.
- Byłoby miło mieć prosty i zwięzły sposób szybkiego definiowania wszystkich opcji i wartości domyślnych w jednym miejscu.
- Byłoby dobrze wspierać samodzielne opcje, które służą jako flagi (brak wartości po opcji).
- Nie wiemy, czy argument jest ujęty w cudzysłów. Nie wiemy też, czy wartość arg została przekazana przy użyciu znaków ucieczki. Lepiej uzyskać dostęp do argumentu za pomocą% ~ 1 i umieścić przypisanie w cudzysłowie. Wówczas partia może polegać na braku ujętych cudzysłowów, ale znaki specjalne są nadal ogólnie bezpieczne bez zmiany znaczenia. (To nie jest kuloodporne, ale radzi sobie w większości sytuacji)
Moje rozwiązanie polega na utworzeniu zmiennej OPTIONS, która definiuje wszystkie opcje i ich wartości domyślne. OPCJE są również używane do testowania, czy podana opcja jest prawidłowa. Ogromna ilość kodu jest zapisywana po prostu poprzez przechowywanie wartości opcji w zmiennych o takich samych nazwach jak opcja. Ilość kodu jest stała, niezależnie od liczby zdefiniowanych opcji; musi się zmienić tylko definicja OPTIONS.
EDYCJA - Ponadto kod pętli: musi ulec zmianie, jeśli zmieni się liczba obowiązkowych argumentów pozycyjnych. Na przykład, często wszystkie argumenty są nazwane, w takim przypadku chcesz przeanalizować argumenty zaczynające się od pozycji 1 zamiast 3. Zatem w pętli: wszystkie 3 stają się 1, a 4 staje się 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!
Naprawdę nie ma tak dużo kodu. Większość powyższego kodu to komentarze. Oto dokładnie ten sam kod, bez komentarzy.
@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!
To rozwiązanie zapewnia argumenty w stylu uniksowym w ramach wsadu systemu Windows. Nie jest to norma dla systemu Windows - batch zwykle ma opcje poprzedzające wymagane argumenty, a opcje są poprzedzone przedrostkiem /
.
Techniki zastosowane w tym rozwiązaniu można łatwo dostosować do stylu opcji systemu Windows.
- Pętla analizująca zawsze szuka opcji na
%1
i kontynuuje, dopóki arg 1 nie zacznie się od/
- Zwróć uwagę, że przypisania SET muszą być umieszczone w cudzysłowach, jeśli nazwa zaczyna się od
/
.
SET /VAR=VALUE
nie
SET "/VAR=VALUE"
działa. I tak już to robię w moim rozwiązaniu.
- Standardowy styl Windows wyklucza możliwość pierwszej wymaganej wartości argumentu zaczynającej się od
/
. To ograniczenie można wyeliminować, stosując niejawnie zdefiniowaną //
opcję, która służy jako sygnał do wyjścia z pętli analizowania opcji. Nic nie zostanie zapisane dla //
„opcji”.
Aktualizacja 2015-12-28: Wsparcie dla !
wartości opcji
W powyższym kodzie każdy argument jest rozwijany, podczas gdy opóźnione rozwijanie jest włączone, co oznacza, że !
najprawdopodobniej jest usuwany lub coś podobnego !var!
jest rozwijane. Ponadto ^
można go usunąć, jeśli !
jest obecny. Następująca mała modyfikacja kodu bez komentarza usuwa takie ograniczenie !
i ^
jest zachowywana w wartościach opcji.
@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!