Windows Bat файл необязательный разбор аргументов


Мне нужно, чтобы мой файл bat принимал несколько необязательных именованных аргументов.

mycmd.bat man1 man2 -username alice -otheroption

Я хотел бы быть в состоянии вырвать эти значения в переменные.

просто звоните всем, кто уже решил эту проблему. Человек эти летучие мыши файлы боль.

5 64

5 ответов:

хотя я склонен согласиться с @AlekDavis' комментарий, тем не менее, есть несколько способов сделать это в оболочке NT.

подход я бы воспользовался SHIFT и если условное ветвление, что-то вроде этого...

@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, а также необходимые аргументы %1 и %2.
  • это становится болью, чтобы иметь блок IF для каждого варианта, тем более, что количество вариантов растет.
  • было бы неплохо иметь простой и лаконичный способ быстро определить все параметры и значения по умолчанию в одном место.
  • было бы неплохо поддерживать автономные параметры, которые служат флагами (без значения после опции).
  • мы не знаем, если arg заключен в кавычки. Мы также не знаем, было ли передано значение arg с использованием экранированных символов. Лучше получить доступ к arg с помощью %~1 и заключить назначение в кавычки. Тогда пакет может полагаться на отсутствие вложенных кавычек, но специальные символы по-прежнему в целом безопасны без экранирования. (Это не пуленепробиваемые, но это обрабатывает большинство ситуаций)

мое решение основано на создании переменной OPTIONS, которая определяет все параметры и их значения по умолчанию. Параметры также используются для проверки допустимости поставляемого параметра. Огромное количество кода сохраняется путем простого хранения значений параметров в переменных, названных так же, как опция. Количество кода является постоянным независимо от того, сколько параметров определено; только определение параметров должно изменение.

EDIT - кроме того, код цикла :должен измениться, если изменяется число обязательных позиционных аргументов. Например, часто все аргументы называются, и в этом случае вы хотите проанализировать аргументы, начинающиеся с позиции 1 вместо 3. Таким образом, в цикле :все 3 становятся 1, а 4 становится 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!

там действительно не так много кода. Большая часть кода выше комментариев. Вот точно такой же код, без комментарий.

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


Это решение предоставляет аргументы стиля Unix в пакете Windows. Это не норма для Windows-пакет обычно имеет параметры, предшествующие требуемым Аргументам, и параметры имеют префикс /.

методы, используемые в этом решении, легко адаптируются к стилю опций Windows.

  • цикл синтаксического анализа всегда ищет опцию в %1, и это продолжается до тех пор, пока arg 1 не делает начинаются с /
  • обратите внимание, что набор заданий должны заключаются в кавычки, если имя начинается с /.
    SET /VAR=VALUE не
    SET "/VAR=VALUE" строительство. Я уже делаю это в моем решении в любом случае.
  • стандартный стиль Windows исключает возможность первого требуемого значения аргумента, начинающегося с /. Это ограничение может быть устранено с помощью неявно определенного // опция, которая служит в качестве сигнал для выхода из цикла разбора параметров. Ничего не будет храниться для // "опции".


обновление 2015-12-28: поддержка ! В значения параметров

в приведенном выше коде каждый аргумент расширяется, а отложенное расширение включено, что означает, что ! чаще всего лишен, или еще что-то вроде !var! расширяется. Кроме того, ^ также может быть лишен, если ! присутствует. Этот после небольшого изменения в коде без комментариев удаляет ограничение таким образом, что ! и ^ сохраняются в значениях параметров.

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

Если вы хотите использовать необязательные аргументы, но не именованные аргументы, то этот подход работал для меня. Я думаю, что это гораздо проще код, чтобы следовать.

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%

вот аргументы парсера. Вы можете смешивать любые строковые аргументы (сохраненные нетронутыми) или экранированные параметры (одиночные или пары параметров/значений). Чтобы проверить его раскомментировать последние 2 оператора и запустить как:

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

Escape char определяется как"SEP=/", пересмотреть при необходимости.

@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 /test=123 /test str anystr3
rem set | find "test$"
rem echo %1 %2 %3 %4 %5 %6 %7 %8 %9

:EXIT

однажды я написал программу, которая обрабатывает короткие (- h), длинные (--help) и непараметрические аргументы в пакетном файле. Эта техника включает в себя:

  • аргументы без опции, за которыми следуют аргументы опции.

  • оператор сдвига для тех параметров, которые не имеют аргумента типа '--help'.

  • два оператора временного сдвига для тех параметров, которые требуют аргумента.

  • цикл через метку для обработка всех аргументов командной строки.

  • выйдите из скрипта и остановите обработку для тех параметров, которые не требуют дальнейших действий, таких как '--help'.

  • написал справочные функции для пользователя guidiness

вот мой код.

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