Argomento facoltativo dell’argomento del file Bat di Windows

Ho bisogno del mio file bat per accettare più argomenti con nome opzionale.

mycmd.bat man1 man2 -username alice -otheroption 

Per esempio il mio comando ha 2 parametri obbligatori e due parametri opzionali (-username) che hanno un valore argomento di alice e -opzione:

Mi piacerebbe essere in grado di carpire questi valori in variabili.

Basta fare una telefonata a chiunque abbia già risolto questo problema. Questi file di pipistrelli sono un dolore.

Sebbene io tendo ad essere d’accordo con il commento di @AlekDavis , ci sono comunque diversi modi per farlo nella shell NT.

L’approccio vorrei sfruttare il comando SHIFT e la derivazione condizionale IF , qualcosa come questo …

 @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 

La risposta selezionata funziona, ma potrebbe essere migliorata.

  • Le opzioni dovrebbero probabilmente essere inizializzate su valori predefiniti.
  • Sarebbe bello conservare% 0 e gli argomenti richiesti% 1 e% 2.
  • Diventa doloroso avere un blocco IF per ogni opzione, specialmente con il crescere del numero di opzioni.
  • Sarebbe bello avere un modo semplice e conciso per definire rapidamente tutte le opzioni e le impostazioni predefinite in un’unica posizione.
  • Sarebbe opportuno supportare le opzioni autonome che fungono da bandiere (nessun valore che segue l’opzione).
  • Non sappiamo se un arg è racchiuso tra virgolette. Né sappiamo se è stato passato un valore di arg usando caratteri di escape. È meglio accedere a un argomento utilizzando% ~ 1 e racchiudere il compito tra virgolette. Quindi il batch può contare sull’assenza di racchiudere le virgolette, ma i caratteri speciali sono ancora generalmente sicuri senza fuggire. (Questo non è a prova di proiettile, ma gestisce la maggior parte delle situazioni)

La mia soluzione si basa sulla creazione di una variabile OPTIONS che definisce tutte le opzioni e le loro impostazioni predefinite. OPTIONS viene anche utilizzato per verificare se un’opzione fornita è valida. Una quantità enorme di codice viene salvata semplicemente memorizzando i valori dell’opzione nelle variabili denominate come l’opzione. La quantità di codice è costante indipendentemente dal numero di opzioni definite; solo la definizione OPZIONI deve cambiare.

EDIT – Inoltre, il: codice del ciclo deve cambiare se cambia il numero di argomenti posizionali obbligatori. Ad esempio, spesso vengono denominati tutti gli argomenti, nel qual caso si desidera analizzare gli argomenti che iniziano alla posizione 1 anziché a 3. Quindi all’interno di: loop, tutti 3 diventano 1 e 4 diventa 2.

 @echo off setlocal enableDelayedExpansion :: Define the option names along with default values, using a  :: 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! 

Non c’è davvero tanto codice. La maggior parte del codice sopra riportato è un commento. Ecco lo stesso codice esatto, senza i commenti.

 @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! 

Questa soluzione fornisce argomenti in stile Unix all’interno di un batch di Windows. Questa non è la norma per Windows: il batch di solito ha le opzioni che precedono gli argomenti richiesti e le opzioni sono precedute da / .

Le tecniche utilizzate in questa soluzione sono facilmente adattate per uno stile Windows di opzioni.

  • Il ciclo di analisi cerca sempre un’opzione in %1 e continua fino a quando arg 1 non inizia con /
  • Nota che le assegnazioni SET devono essere racchiuse tra virgolette se il nome inizia con / .
    SET /VAR=VALUE fallisce
    SET "/VAR=VALUE" funziona. Lo sto già facendo nella mia soluzione comunque.
  • Lo stile standard di Windows preclude la possibilità del primo valore di argomento richiesto che inizia con / . Questa limitazione può essere eliminata impiegando un’opzione // implicitamente definita che funge da segnale per uscire dal ciclo di analisi delle opzioni. Niente sarebbe stato memorizzato per // “opzione”.

Aggiornamento 2015-12-28: supporto per ! in valori di opzione

Nel codice sopra, ogni argomento è espanso mentre l’espansione ritardata è abilitata, il che significa che ! sono molto probabilmente spogliati, o qualcosa del genere !var! è espanso. Inoltre, ^ può anche essere spogliato se ! è presente. La seguente piccola modifica al codice non commentato rimuove la limitazione in tal modo ! e ^ sono conservati nei valori di opzione.

 @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! 

Se si desidera utilizzare argomenti facoltativi, ma non argomenti con nome, questo approccio ha funzionato per me. Penso che questo sia un codice molto più semplice da seguire.

 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% 

Ecco gli argomenti parser. Combina qualsiasi argomento di stringa (mantenuto intatto) o opzioni di escape (coppie singole o opzioni / valore). Per testarlo, togliere il commento alle ultime 2 dichiarazioni ed eseguire come:

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

Il carattere di escape è definito come ” SEP = /”, ridefinire se necessario.

 @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 

Una volta avevo scritto un programma che gestiva gli argomenti short (-h), long (–help) e non-option nel file batch. Queste tecniche includono:

  • argomenti non opzionali seguiti da argomenti di opzioni.

  • shift operator per quelle opzioni che non hanno argomenti come ‘–help’.

  • due operatori time shift per quelle opzioni che richiedono un argomento.

  • scorrere un’etichetta per l’elaborazione di tutti gli argomenti della riga di comando.

  • Esci dallo script e interrompi l’elaborazione per quelle opzioni che non richiedono ulteriori azioni come “–help”.

  • Ha scritto le funzioni di guida per la guida dell’utente

Ecco il mio codice.

 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