[batch-file] Windows Bat 파일 선택적 인수 구문 분석

여러 선택적 명명 된 인수를 허용하려면 내 bat 파일이 필요합니다.

mycmd.bat man1 man2 -username alice -otheroption

예를 들어, 내 명령에는 2 개의 필수 매개 변수와 인수 값이 alice 인 2 개의 선택적 매개 변수 (-username) 및 -otheroption이 있습니다.

이 값을 변수로 추출 할 수 있기를 바랍니다.

이미이 문제를 해결 한 사람에게 전화를 걸면됩니다. 이 박쥐 파일은 고통 스럽습니다.



답변

@AlekDavis의 의견 에 동의하는 경향이 있지만 그럼에도 불구하고 NT 쉘에서이를 수행하는 몇 가지 방법이 있습니다.

SHIFT 명령과 IF 조건부 분기를 이용하는 방법은 다음과 같습니다.

@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 블록을 사용하는 것은 고통스러워집니다.
  • 모든 옵션과 기본값을 한곳에서 신속하게 정의 할 수있는 간단하고 간결한 방법이 있으면 좋을 것입니다.
  • 플래그 역할을하는 독립형 옵션을 지원하는 것이 좋습니다 (옵션 다음에 값 없음).
  • 인수가 따옴표로 묶여 있는지 알 수 없습니다. 이스케이프 문자를 사용하여 인수 값이 전달되었는지도 알 수 없습니다. % ~ 1을 사용하여 인수에 액세스하고 할당을 따옴표로 묶는 것이 좋습니다. 그런 다음 일괄 처리는 따옴표를 묶지 않는 것에 의존 할 수 있지만 특수 문자는 일반적으로 이스케이프 없이도 안전합니다. (이것은 방탄이 아니지만 대부분의 상황을 처리합니다)

내 솔루션은 모든 옵션과 기본값을 정의하는 OPTIONS 변수의 생성에 의존합니다. OPTIONS는 제공된 옵션이 유효한지 테스트하는데도 사용됩니다. 옵션과 동일한 이름의 변수에 옵션 값을 저장하기 만하면 엄청난 양의 코드가 절약됩니다. 코드의 양은 정의 된 옵션 수에 관계없이 일정합니다. OPTIONS 정의 만 변경하면됩니다.

편집 -또한 필수 위치 인수의 수가 변경되면 : loop 코드도 변경되어야합니다. 예를 들어, 종종 모든 인수의 이름이 지정됩니다.이 경우 3이 아닌 위치 1에서 시작하는 인수를 구문 분석하려고합니다. 따라서 : loop 내에서 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!

이 솔루션은 Windows 배치 내에서 Unix 스타일 인수를 제공합니다. 이것은 Windows의 표준이 아닙니다. 일반적으로 필요한 인수 앞에 옵션이 있고 옵션 앞에 /.

이 솔루션에 사용 된 기술은 Windows 스타일의 옵션에 쉽게 적용됩니다.

  • 구문 분석 루프는 항상에서 옵션을 찾고 %1arg 1이 다음으로 시작하지 않을 때까지 계속됩니다./
  • 이름이로 시작하는 경우 SET 할당 따옴표로 묶어야합니다 /. 작동
    SET /VAR=VALUE하지 않습니다
    SET "/VAR=VALUE". 어쨌든 내 솔루션에서 이미 이것을하고 있습니다.
  • 표준 Windows 스타일은로 시작하는 첫 번째 필수 인수 값의 가능성을 배제합니다 /. 이 제한은 //옵션 구문 분석 루프를 종료하는 신호 역할 을하는 암시 적으로 정의 된 옵션을 사용하여 제거 할 수 있습니다 . //“옵션” 에 대해서는 아무것도 저장되지 않습니다 .

2015-12-28 업데이트 :! in 옵션 값 지원

위의 코드에서 각 인수는 확장되고 지연된 확장이 활성화되어 있습니다. 즉, !대부분 제거되거나 다른 항목 !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%


답변

동적 변수 생성

여기에 이미지 설명 입력

장점

  • 9 개 이상의 인수에 대해 작동
  • 유지 %1,, %2%* 재치에
  • 스타일 /arg-arg스타일 모두에 적합
  • 논쟁에 대한 사전 지식 없음
  • 구현은 메인 루틴과 별개입니다.

단점

  • 오래된 인수는 연속 실행으로 누출 될 수 있으므로 setlocal로컬 범위 지정에 사용하거나 동반 :CLEAR-ARGS루틴을 작성하십시오!
  • 아직 별명 지원 (등 --force-f)
  • ""인수 지원 없음

용법

다음은 다음 인수가 .bat 변수와 관련되는 방식의 예입니다.

>> testargs.bat /b 3 -c /d /e /f /g /h /i /j /k /bar 5 /foo "c:\"

echo %*        | /b 3 -c /d /e /f /g /h /i /j /k /bar 5 /foo "c:\"
echo %ARG_FOO% | c:\
echo %ARG_A%   |
echo %ARG_B%   | 3
echo %ARG_C%   | 1
echo %ARG_D%   | 1

이행

@echo off
setlocal

CALL :ARG-PARSER %*

::Print examples
echo: ALL: %*
echo: FOO: %ARG_FOO%
echo: A:   %ARG_A%
echo: B:   %ARG_B%
echo: C:   %ARG_C%
echo: D:   %ARG_C%


::*********************************************************
:: Parse commandline arguments into sane variables
:: See the following scenario as usage example:
:: >> thisfile.bat /a /b "c:\" /c /foo 5
:: >> CALL :ARG-PARSER %*
:: ARG_a=1
:: ARG_b=c:\
:: ARG_c=1
:: ARG_foo=5
::*********************************************************
:ARG-PARSER
    ::Loop until two consecutive empty args
    :loopargs
        IF "%~1%~2" EQU "" GOTO :EOF

        set "arg1=%~1"
        set "arg2=%~2"
        shift

        ::Allow either / or -
        set "tst1=%arg1:-=/%"
        if "%arg1%" NEQ "" (
            set "tst1=%tst1:~0,1%"
        ) ELSE (
            set "tst1="
        )

        set "tst2=%arg2:-=/%"
        if "%arg2%" NEQ "" (
            set "tst2=%tst2:~0,1%"
        ) ELSE (
            set "tst2="
        )


        ::Capture assignments (eg. /foo bar)
        IF "%tst1%" EQU "/"  IF "%tst2%" NEQ "/" IF "%tst2%" NEQ "" (
            set "ARG_%arg1:~1%=%arg2%"
            GOTO loopargs
        )

        ::Capture flags (eg. /foo)
        IF "%tst1%" EQU "/" (
            set "ARG_%arg1:~1%=1"
            GOTO loopargs
        )
    goto loopargs
GOTO :EOF


답변

일단 배치 파일에서 짧은 (-h), 긴 (–help) 및 비 옵션 인수를 처리하는 프로그램을 작성했습니다. 이 기술에는 다음이 포함됩니다.

  • 옵션 인수가 뒤 따르는 비 옵션 인수.

  • ‘–help’와 같은 인수가없는 옵션에 대한 시프트 연산자.

  • 인수가 필요한 옵션에 대한 두 개의 시간 이동 연산자.

  • 모든 명령 줄 인수를 처리하기 위해 레이블을 반복합니다.

  • ‘–help’와 같은 추가 조치가 필요하지 않은 옵션에 대한 스크립트를 종료하고 처리를 중지하십시오.

  • 사용자 안내를위한 도움말 기능 작성

다음은 내 코드입니다.

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


답변

다음은 인수 파서입니다. 문자열 인수 (손상되지 않은 상태로 유지) 또는 이스케이프 된 옵션 (단일 또는 옵션 / 값 쌍)을 혼합 할 수 있습니다. 테스트하려면 마지막 2 개의 문을 주석 해제하고 다음과 같이 실행하십시오.

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

이스케이프 문자는로 정의되며 "_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$1 /test$2=123 /test$3 str anystr3
rem set | find "test$"
rem echo %1 %2 %3 %4 %5 %6 %7 %8 %9

:EXIT


답변