2010-10-20 9 views
59

Potrzebuję mojego pliku bat, aby zaakceptować wiele opcjonalnych nazwanych argumentów.Plik Windows Bat opcjonalny argument parsowania

mycmd.bat man1 man2 -username alice -otheroption 

Na przykład moja komenda ma 2 obowiązkowych parametrów oraz dwa opcjonalne parametry (-username), który ma wartość argumentu Alicji i -otheroption:

Chciałbym, aby móc je zrywać wartości do zmiennych.

Po prostu dzwonisz do każdego, kto już to rozwiązał. Człowieku te pliki nietoperzy to ból.

+0

Ta strona jest obiecujący: http://www.pcguide.com/vb/showthread.php?t=52323 –

+10

Każdy powód dlaczego trzeba to zrobić w pliku BAT i nie mówią w języku VBScript? Być może istnieje sposób, aby to zrobić w pliku BAT, ale pozostając przy podejściu do pliku BAT, "wkraczasz w świat bólu, synu". :-) –

+0

Jeszcze lepiej jest używać PowerShell. Ma bardzo zaawansowane i wbudowane zarządzanie parametrami. –

Odpowiedz

81

Chociaż zgadzam się z @AlekDavis' comment, istnieje jednak kilka sposobów, aby to zrobić w powłoce NT.

Podejście Chciałbym skorzystać z komendy SHIFT i IF warunkowego rozgałęzienia coś takiego ...

@ECHO OFF 

SET man1=%1 
SET man2=%2 
SHIFT & SHIFT 

:loop 
IF NOT "%1"=="" (
    IF "%1"=="-username" (
     SET user=%2 
     SHIFT 
    ) 
    IF "%1"=="-otheroption" (
     SET other=%2 
     SHIFT 
    ) 
    SHIFT 
    GOTO :loop 
) 

ECHO Man1 = %man1% 
ECHO Man2 = %man2% 
ECHO Username = %user% 
ECHO Other option = %other% 

REM ...do stuff here... 

:theend 
+0

Dzięki ewall - jeden mały błąd w linii 4 - SET man2 =% 1 –

+1

'SHIFT/2' przesuwa się począwszy od arg 2. Nie przesuwa się dwa razy. Czy nie należy go zastąpić 'SHIFT & SHIFT'? – dbenham

+0

@dbenham Tak czy inaczej składnia zadziała, najwyraźniej. – ewall

48

Wybrana odpowiedź działa, ale może to wykorzystać pewną poprawę.

  • Prawdopodobnie opcje powinny zostać zainicjalizowane na wartości domyślne.
  • Byłoby miło zachować% 0, a także wymagane argumenty% 1 i% 2.
  • Staje się bólem mieć blok IF dla każdej opcji, zwłaszcza, że ​​liczba opcji rośnie.
  • Byłoby miło mieć prosty i zwięzły sposób szybkiego zdefiniowania wszystkich opcji i ustawień domyślnych w jednym miejscu.
  • Byłoby dobrze wspierać autonomiczne opcje, które służą jako flagi (brak wartości po opcji).
  • Nie wiemy, czy argument jest zawarty w ofertach. Nie wiemy również, czy wartość arg została przekazana przy użyciu znaków zbiegów. Lepiej uzyskać dostęp do argumentu za pomocą% ~ 1 i zamknąć zadanie w cudzysłowie. Następnie partia może polegać na braku cytatów, ale znaki specjalne są zazwyczaj bezpieczne bez ucieczki. (Nie jest to punktor kuloodporny, ale obsługuje większość sytuacji)

Moje rozwiązanie polega na utworzeniu zmiennej OPTIONS, która definiuje wszystkie opcje i ich ustawienia domyślne. OPTIONS służy również do sprawdzenia, czy podana opcja jest poprawna. Ogromna ilość kodu jest zapisywana po prostu poprzez przechowywanie wartości opcji w zmiennych o tej samej nazwie co opcja. Ilość kodu jest stała bez względu na liczbę zdefiniowanych opcji; tylko definicja OPCJE musi się zmienić.

EDIT- Również: Kod pętla musi się zmienić, jeśli liczba obowiązkowych pozycyjnych argumentów zmian. Na przykład, często razy wszystkie argumenty są nazwane, w którym to przypadku chcesz przeanalizować argumenty zaczynają w pozycji 1 zamiast 3. Dlatego w: pętli, wszystkie 3 stają się 1 i 4 staje 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 zbyt wiele 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 Unix argumenty w stylu partii Windows. Nie jest to normą dla systemu Windows - partia ma zwykle opcje poprzedzające wymagane argumenty, a opcje są poprzedzone prefiksem /.

Techniki stosowane w tym rozwiązaniu można łatwo dostosować do opcji stylu systemu Windows.

  • Pętla parsowanie zawsze szuka opcji na %1, a to trwa aż arg 1 nie rozpoczyna się /
  • Uwaga, które wyznaczają zadania musi być ujęty w cudzysłów, jeśli nazwa zaczyna się / .
    zawiedzie
    SET "/VAR=VALUE" działa. W każdym razie robię to już w moim rozwiązaniu.
  • Standardowy styl Windows wyklucza możliwość pierwszej wymaganej wartości argumentu rozpoczynającej się od /. Ograniczenie to można wyeliminować, wykorzystując niejawnie zdefiniowaną opcję //, która służy jako sygnał do wyjścia z pętli analizowania opcji. Nic nie będzie przechowywane dla "opcji" //.


Aktualizacja 28.12.2015: Wsparcie ! w opcji wartości

W powyższym kodzie, każdy argument jest rozszerzany podczas opóźniony rozbudowa jest włączona, co oznacza, że ​​! są najprawdopodobniej usunięte, albo coś takiego jak !var! jest rozszerzone. Ponadto, ^ można również usunąć, jeśli występuje !. Następująca niewielka modyfikacja niezredagowanego kodu usuwa ograniczenie tak, że ! i ^ są zachowane 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! 
+0

Dzięki za bardzo eleganckie rozwiązanie, lubię to bardziej niż ten, który został przyjęty jako odpowiedź. Jedyne, co przegapiłeś, to jak używać nazwanych parametrów w skrypcie wsadowym, na przykład echo% -username% – Roboblob

+2

+1 Ładnie zrobione. Nazwiemy to ... 'getopts.bat' –

+1

Bardzo eleganckie rozwiązanie. Jeśli rozważasz użycie tego, pamiętaj, że podany kod zacznie analizować argument 3. Nie jest to, czego chcesz w ogólnym przypadku. Zamień "3" na "1", aby to naprawić. @dbenham byłoby miło, gdybyś mógł to bardziej oczywiste w oryginalnym poście, prawdopodobnie nie jestem jedynym, który był początkowo zdezorientowany. – ocroquette

13

Jeśli chcesz użyć argumentów opcjonalnych, ale nie nazwanych argumentów, to podejście zadziałało dla mnie. Myślę, że jest to o wiele łatwiejszy kod do naśladowania.

REM Get argument values. If not specified, use default values. 
IF "%1"=="" (SET "DatabaseServer=localhost") ELSE (SET "DatabaseServer=%1") 
IF "%2"=="" (SET "DatabaseName=MyDatabase") ELSE (SET "DatabaseName=%2") 

REM Do work 
ECHO Database Server = %DatabaseServer% 
ECHO Database Name = %DatabaseName% 
+0

'JEŻELI"% 1 "==" "nie powiedzie się, jeśli argument zawiera same cytaty. Po prostu użyj dowolnego innego nietypowego symbolu, np. ._ = [] # itd. – Fr0sT

+2

Nie sądzę, żeby to działało, chyba że użytkownik użyje "" zamiast argumentu. W przeciwnym razie, jak mogę ustawić nazwę bazy danych, ale pozostawić serwer DB pusty? –

+0

Jak inaczej widzisz inną odpowiedź? @SeanLong – sehe

0

Oto parser argumentów. Miksujesz dowolne argumenty ciągłe (nie naruszone) lub zmienione opcje (pary pojedyncze lub pary opcji/wartości). Aby przetestować Odkomentuj trwać 2 oświadczenia i uruchom jako:

getargs anystr1 anystr2/test $ 1/Test $ 2 = 123/test $ 3 str anystr3

Ucieczka char jest definiowany jako "września = /", przedefiniować jeśli potrzebne.

@echo off 

REM Command line argument parser. Format (both "=" and "space" separators are supported): 
REM anystring1 anystring2 /param1 /param2=value2 /param3 value3 [...] anystring3 anystring4 
REM Returns enviroment variables as: 
REM param1=1 
REM param2=value2 
REM param3=value3 
REM Leading and traling strings are preserved as %1, %2, %3 ... %9 parameters 
REM but maximum total number of strings is 9 and max number of leading strings is 8 
REM Number of parameters is not limited! 

set _CNT_=1 
set _SEP_=/ 

:PARSE 

if %_CNT_%==1 set _PARAM1_=%1 & set _PARAM2_=%2 
if %_CNT_%==2 set _PARAM1_=%2 & set _PARAM2_=%3 
if %_CNT_%==3 set _PARAM1_=%3 & set _PARAM2_=%4 
if %_CNT_%==4 set _PARAM1_=%4 & set _PARAM2_=%5 
if %_CNT_%==5 set _PARAM1_=%5 & set _PARAM2_=%6 
if %_CNT_%==6 set _PARAM1_=%6 & set _PARAM2_=%7 
if %_CNT_%==7 set _PARAM1_=%7 & set _PARAM2_=%8 
if %_CNT_%==8 set _PARAM1_=%8 & set _PARAM2_=%9 

if "%_PARAM2_%"=="" set _PARAM2_=1 

if "%_PARAM1_:~0,1%"=="%_SEP_%" (
    if "%_PARAM2_:~0,1%"=="%_SEP_%" (
    set %_PARAM1_:~1,-1%=1 
    shift /%_CNT_% 
) else (
    set %_PARAM1_:~1,-1%=%_PARAM2_% 
    shift /%_CNT_% 
    shift /%_CNT_% 
) 
) else (
    set /a _CNT_+=1 
) 

if /i %_CNT_% LSS 9 goto :PARSE 

set _PARAM1_= 
set _PARAM2_= 
set _CNT_= 

rem getargs anystr1 anystr2 /test$1 /test$2=123 /test$3 str anystr3 
rem set | find "test$" 
rem echo %1 %2 %3 %4 %5 %6 %7 %8 %9 

:EXIT 
0

Raz napisałem program, który obsługuje argumenty krótkie (-h), długie (--help) i nieopcjonalne w pliku wsadowym. Technika ta obejmuje:

  • argumenty będące opcjami, a za nim argumentów opcjonalnych.

  • operator zmiany dla tych opcji, które nie mają argumentów takich jak "--help".

  • operator dwóch przesunięć czasowych dla tych opcji, które wymagają argumentu.

  • przepuszcza pętlę przez etykietę w celu przetworzenia wszystkich argumentów wiersza poleceń.

  • Zakończ skrypt i zatrzymaj przetwarzanie dla opcji, które nie wymagają dalszych działań, takich jak "--help".

  • Wrote funkcje pomocnicze dla guidiness użytkownika

Oto mój kod.

set BOARD= 
set WORKSPACE= 
set CFLAGS= 
set LIB_INSTALL=true 
set PREFIX=lib 
set PROGRAM=install_boards 

:initial 
set result=false 
if "%1" == "-h" set result=true 
if "%1" == "--help" set result=true 
if "%result%" == "true" (
goto :usage 
) 
if "%1" == "-b" set result=true 
if "%1" == "--board" set result=true 
if "%result%" == "true" (
goto :board_list 
) 
if "%1" == "-n" set result=true 
if "%1" == "--no-lib" set result=true 
if "%result%" == "true" (
set LIB_INSTALL=false 
shift & goto :initial 
) 
if "%1" == "-c" set result=true 
if "%1" == "--cflag" set result=true 
if "%result%" == "true" (
set CFLAGS=%2 
if not defined CFLAGS (
echo %PROGRAM%: option requires an argument -- 'c' 
goto :try_usage 
) 
shift & shift & goto :initial 
) 
if "%1" == "-p" set result=true 
if "%1" == "--prefix" set result=true 
if "%result%" == "true" (
set PREFIX=%2 
if not defined PREFIX (
echo %PROGRAM%: option requires an argument -- 'p' 
goto :try_usage 
) 
shift & shift & goto :initial 
) 

:: handle non-option arguments 
set BOARD=%1 
set WORKSPACE=%2 

goto :eof 


:: Help section 

:usage 
echo Usage: %PROGRAM% [OPTIONS]... BOARD... WORKSPACE 
echo Install BOARD to WORKSPACE location. 
echo WORKSPACE directory doesn't already exist! 
echo. 
echo Mandatory arguments to long options are mandatory for short options too. 
echo -h, --help     display this help and exit 
echo -b, --boards     inquire about available CS3 boards 
echo -c, --cflag=CFLAGS   making the CS3 BOARD libraries for CFLAGS 
echo -p. --prefix=PREFIX   install CS3 BOARD libraries in PREFIX 
echo        [lib] 
echo -n, --no-lib     don't install CS3 BOARD libraries by default 
goto :eof 

:try_usage 
echo Try '%PROGRAM% --help' for more information 
goto :eof 
Powiązane problemy