Windows Bat файл необязательный разбор аргументов
Мне нужно, чтобы мой файл bat принимал несколько необязательных именованных аргументов.
mycmd.bat man1 man2 -username alice -otheroption
Я хотел бы быть в состоянии вырвать эти значения в переменные.
просто звоните всем, кто уже решил эту проблему. Человек эти летучие мыши файлы боль.
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