web-dev-qa-db-fra.com

Analyse des arguments facultatifs des fichiers Windows Bat

J'ai besoin que mon fichier bat accepte plusieurs arguments nommés facultatifs.

mycmd.bat man1 man2 -username alice -otheroption

Par exemple, ma commande a 2 paramètres obligatoires et deux paramètres facultatifs (-username) qui ont une valeur d'argument de alice et -otheroption:

Je voudrais être en mesure de plumer ces valeurs dans des variables.

Il suffit d'envoyer un appel à quiconque a déjà résolu le problème. Man ces fichiers de chauve-souris sont une douleur.

66
chickeninabiscuit

Bien que j'aie tendance à être d'accord avec le commentaire de @AlekDavis , il existe néanmoins plusieurs façons de procéder dans le shell NT.

Je prendrais avantage de la commande SHIFT et du IF branchement conditionnel, quelque chose comme ceci ...

@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
96
ewall

La réponse sélectionnée fonctionne, mais elle pourrait être améliorée.

  • Les options devraient probablement être initialisées aux valeurs par défaut.
  • Il serait bien de conserver% 0 ainsi que les arguments requis% 1 et% 2.
  • Il devient difficile d’avoir un bloc SI pour chaque option, en particulier à mesure que le nombre d’options augmente.
  • Il serait bien d’avoir un moyen simple et concis de définir rapidement toutes les options et les valeurs par défaut au même endroit.
  • Il serait bon de prendre en charge les options autonomes qui servent d'indicateurs (aucune valeur après l'option).
  • Nous ne savons pas si un argument est entre guillemets. Nous ne savons pas non plus si une valeur arg a été transmise à l'aide de caractères d'échappement. Il est préférable d’accéder à un argument utilisant% ~ 1 et de placer l’affectation entre guillemets. Ensuite, le lot peut s’appuyer sur l’absence de guillemets, mais les caractères spéciaux restent généralement sûrs sans s’échapper. (Ceci n'est pas à l'épreuve des balles, mais il gère la plupart des situations)

Ma solution repose sur la création d'une variable OPTIONS qui définit toutes les options et leurs valeurs par défaut. OPTIONS est également utilisé pour vérifier si une option fournie est valide. Une énorme quantité de code est sauvegardée en stockant simplement les valeurs d'option dans des variables portant le même nom que l'option. La quantité de code est constante quel que soit le nombre d'options définies. seule la définition des OPTIONS doit être modifiée.

EDIT- De plus, le code de la boucle: doit changer si le nombre d'arguments de position obligatoires change. Par exemple, souvent, tous les arguments sont nommés, auquel cas vous souhaitez analyser les arguments commençant à la position 1 au lieu de 3. Ainsi, dans la boucle:, les 3 deviennent 1 et 4 devient 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!

Il n'y a vraiment pas beaucoup de code. La plupart du code ci-dessus sont des commentaires. Voici le même code exact, sans les commentaires.

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


Cette solution fournit des arguments de style Unix dans un lot Windows. Ce n'est pas la norme pour Windows - batch contient généralement les options précédant les arguments requis et les options sont préfixées de /.

Les techniques utilisées dans cette solution s’adaptent facilement à un style d’options Windows.

  • La boucle d'analyse recherche toujours une option à %1 et continue jusqu'à ce que arg 1 ne commence pas par /
  • Notez que SET assignmentsdoitêtre entre guillemets si le nom commence par /.
    SET /VAR=VALUE échoue
    SET "/VAR=VALUE" fonctionne. Je le fais déjà dans ma solution de toute façon.
  • Le style Windows standard exclut la possibilité que le premier argument requis commence par /. Cette limitation peut être éliminée en utilisant une option // définie implicitement qui sert de signal pour quitter la boucle d'analyse des options. Rien ne serait stocké pour l'option // "option" .


Mise à jour 2015-12-28: Prise en charge de ! dans les valeurs d'option

Dans le code ci-dessus, chaque argument est développé tant que l'expansion retardée est activée, ce qui signifie que ! sont probablement supprimés ou que quelque chose comme !var! est développé. De plus, ^ peut également être supprimé si ! est présent. La petite modification suivante au code non commenté supprime la limitation telle que ! et ^ sont conservés dans les valeurs d'option.

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

Si vous voulez utiliser des arguments optionnels, mais pas des arguments nommés, cette approche a fonctionné pour moi. Je pense que ce code est beaucoup plus facile à suivre.

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%
17
charlie.mott

Une fois, j’avais écrit un programme qui gère les arguments courts (-h), longs (--help) et sans option dans un fichier de commandes . 

  • arguments non-option suivis d'un argument option.

  • opérateur de décalage pour les options sans argument comme '--help'.

  • deux opérateurs de décalage temporel pour les options nécessitant un argument.

  • parcourir une étiquette pour traiter tous les arguments de la ligne de commande.

  • Quittez le script et arrêtez le traitement pour les options qui ne nécessitent aucune action supplémentaire telle que '--help'.

  • A écrit des fonctions d'aide pour guider l'utilisateur 

Voici mon code.

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
1
Equation Solver

Voici des arguments analyseur. Vous pouvez mélanger des arguments de chaîne (conservés intacts) ou des options échappées (paires simples ou option/valeur). Pour le tester, décommentez les 2 dernières déclarations et exécutez-la en tant que:

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

Le caractère d'échappement est défini comme "SEP= /", redéfinissez-le si nécessaire.

@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
1
Oscar