명령줄 파싱
명령줄 파싱 (Command-Line Parsing)
명령줄 인자를 편리하게 파싱하려면 command-line 매크로나 parse-command-line 함수를 사용해요. 이 문서에서는 racket/cmdline 라이브러리가 제공하는 명령줄 파싱 기능의 문법과 동작을 설명합니다.
출처: Racket Reference
본문
(require racket/cmdline) package: base
이 섹션에 문서화된 바인딩들은 racket/cmdline과 racket 라이브러리가 제공합니다. racket/base는 제공하지 않아요.
syntax
(command-line optional-name-expr optional-argv-expr
flag-clause ...
finish-clause)
optional-name-expr =
| #:program name-expr
optional-argv-expr =
| #:argv argv-expr
flag-clause = #:multi flag-spec ...
| #:once-each flag-spec ...
| #:once-any flag-spec ...
| #:final flag-spec ...
| #:usage-help string ...
| #:help-labels string ...
| #:ps string ...
flag-spec = (flags id ... help-spec body ...+)
| (flags => handler-expr help-expr)
flags = flag-string
| (flag-string ...+)
help-spec = string
| (string-expr ...+)
finish-clause =
| #:args arg-formals body ...+
| #:handlers handlers-exprs
arg-formals = rest-id
| (arg ...)
| (arg ...+ . rest-id)
arg = id
| [id default-expr]
handlers-exprs = finish-expr arg-strings-expr
| finish-expr arg-strings-expr help-expr
| finish-expr arg-strings-expr help-expr unknown-expr
flag-clause들의 명세에 따라 명령줄을 파싱합니다.
name-expr은, 제공되면, 명령줄이 잘못되었을 때 오류를 보고하기 위한 프로그램 이름으로 사용될 경로(path) 또는 문자열을 만들어야 합니다. 기본값은 (find-system-path 'run-file)입니다. 경로가 제공되면, 오류를 보고할 때 경로의 마지막 요소만 사용됩니다.
argv-expr은, 제공되면, 문자열들의 리스트 또는 벡터로 평가되어야 합니다. 기본값은 (current-command-line-arguments)입니다.
명령줄은 플래그(각각 플래그 특정 인자를 가질 수 있음)와 그 뒤의 (비-플래그) 인자들로 분해됩니다. - 또는 +로 시작하는 명령줄 문자열은 플래그로 파싱되지만, 플래그에 대한 인자들은 결코 플래그로 파싱되지 않으며, -나 +로 시작하는 정수와 십진수도 플래그로 취급되지 않습니다. 명령줄의 비-플래그 인자들은 모든 플래그와 플래그 인자들 뒤에 나타나야 합니다. 첫 번째 비-플래그 인자 이후의 어떤 명령줄 문자열도 플래그로 파싱되지 않습니다. 내장 -- 플래그는 명령줄 플래그의 끝을 알립니다; -- 플래그 이후의 어떤 명령줄 문자열도 비-플래그 인자로 파싱됩니다.
#:multi, #:once-each, #:once-any, 또는 #:final 절은 일련의 명령줄 플래그 명세를 도입합니다. 절 태그는 플래그가 명령줄에 몇 번 나타날 수 있는지 나타냅니다:
#:multi— 집합에서 지정된 각 플래그는 명령줄에서 어떤 횟수든 나타날 수 있습니다; 즉 집합의 플래그들은 독립적이고 각 플래그는 여러 번 사용될 수 있습니다.#:once-each— 집합에서 지정된 각 플래그는 명령줄에서 한 번만 나타날 수 있습니다; 즉 집합의 플래그들은 독립적이지만 각 플래그는 최대 한 번 지정되어야 합니다. 플래그 명세가 명령줄에서 한 번보다 많이 나타나면exn:fail예외가 일어납니다.#:once-any— 집합에서 지정된 단 하나의 플래그만 명령줄에서 나타날 수 있습니다; 즉 집합의 플래그들은 상호 배타적입니다. 집합이 명령줄에서 한 번보다 많이 나타나면exn:fail예외가 일어납니다.#:final—#:multi와 같지만, 플래그 이후의 어떤 인자도 플래그로 취급되지 않습니다. 여러#:final플래그는 짧은 이름을 가지면 지정될 수 있습니다. 예를 들어-a가#:final플래그라면,-aa는 단일 명령줄 인자 안에서 두 개의-a인스턴스를 결합합니다.
일반적인 플래그 명세는 네 부분으로 구성됩니다:
flags— 플래그 문자열, 또는 플래그 문자열들의 집합. 플래그 집합이 제공되면, 모든 플래그들은 동등합니다. 각 플래그 문자열은 어떤 문자x에 대해"-x"또는"+x"형태, 또는 어떤 문자 시퀀스x에 대해"--x"또는"++x"형태여야 합니다.x는 (부호 있는) 숫자가 플래그로 취급되지 않으므로, 숫자만 또는 숫자와 단일 소수점만 포함할 수 없습니다. 또한"--","-h","--help"플래그는 미리 정의되어 있어 변경할 수 없습니다.ids— 플래그의 인자에 바인딩되는 식별자들. 식별자들의 수는 플래그와 함께 명령줄에 제공될 수 있는 인자 수를 결정하며, 이 식별자들의 이름은 플래그를 설명하는 도움말 메시지에 나타납니다.ids는 플래그를 처리하기 위한body들 안에서 문자열 값에 바인딩됩니다.help-spec— 플래그를 설명하는 문자열 또는 문자열 시퀀스. 이 문자열은 내장-h(또는--help) 플래그의 핸들러에 의해 생성되는 도움말 메시지에서 사용됩니다. 단일 리터럴 문자열이 제공되거나, 문자열을 만드는 임의의 수의 표현식이 제공될 수 있습니다; 후자의 경우 첫 번째 이후의 문자열들은 다음 줄들에 표시됩니다.body들 — 명령줄에 플래그 중 하나가 나타날 때 평가되는 표현식들. 플래그들은 왼쪽에서 오른쪽으로 파싱되고, 각body시퀀스는 대응하는 플래그가 만나질 때 평가됩니다.body들이 평가될 때, 앞의ids는 명령줄에서 플래그를 위해 제공된 인자들에 바인딩됩니다.
=>를 사용하는 플래그 명세는 핸들러와 도움말 문자열을 지정하는 더 일반적인 방법으로 탈출합니다. 이 경우, handler-expr과 help-expr이 반환하는 핸들러 프로시저와 도움말 문자열 리스트가 parse-command-line의 table 인자에서처럼 사용됩니다.
#:usage-help 절은 사용(usage) 줄 바로 뒤에 텍스트 줄들을 삽입합니다. 절의 각 문자열은 별도의 텍스트 줄을 제공합니다.
#:help-labels 절은 명령줄 플래그의 도움말 표(help table)에 텍스트 줄들을 삽입합니다. 절의 각 문자열은 별도의 텍스트 줄을 제공합니다.
#:ps 절은 도움말 출력의 끝에 텍스트 줄들을 삽입합니다. 절의 각 문자열은 별도의 텍스트 줄을 제공합니다.
플래그 절들 뒤에는, 플래그로 파싱되지 않는 명령줄 인자를 처리하는 최종 절(finish clause)이 옵니다:
- 최종 절을 제공하지 않는 것은
#:args () (void)를 제공하는 것과 같습니다. #:args최종 절의 경우,arg-formals의 식별자들은 남은 명령줄 문자열에,lambda표현식에서 식별자가 바인딩되는 것과 같은 방식으로 바인딩됩니다. 따라서 (괄호 없는) 단일id를 지정하면 남은 모든 인자들을 리스트로 모읍니다.arg-formals명세의 유효 애리티(arity)는 사용자가 제공할 수 있는 추가 명령줄 인자의 수를 결정하고,arg-formals의 식별자 이름은 도움말 문자열에서 사용됩니다. 명령줄이 파싱될 때, 제공된 인자 수가arg-formals의 식별자와 맞지 않으면exn:fail예외가 일어납니다. 그렇지 않으면, args 절의body들이 남은 인자를 처리하기 위해 평가되고, 마지막body의 결과가command-line표현식의 결과입니다.#:handlers최종 절은 남은 인자를 처리하는 더 일반적인 방법으로 탈출합니다. 이 경우, 표현식들의 값들은parse-command-line의 마지막 두 개에서 네 개의 인자처럼 사용됩니다.
예시:
(define verbose-mode (make-parameter #f))
(define profiling-on (make-parameter #f))
(define optimize-level (make-parameter 0))
(define link-flags (make-parameter null))
(define file-to-compile
(command-line
#:program "compiler"
#:once-each
[("-v" "--verbose") "Compile with verbose messages"
(verbose-mode #t)]
[("-p" "--profile") "Compile with profiling"
(profiling-on #t)]
#:once-any
[("-o" "--optimize-1") "Compile with optimization level 1"
(optimize-level 1)]
["--optimize-2" ( ; show help on separate lines
"Compile with optimization level 2,"
"which includes all of level 1")
(optimize-level 2)]
#:multi
[("-l" "--link-flags") lf ; flag takes one argument
"Add a flag <lf> for the linker"
(link-flags (cons lf (link-flags)))]
#:args (filename) ; expect one command-line argument: <filename>
; return the argument as a filename to compile
filename))
procedure
(parse-command-line name argv table finish-proc arg-help-strs
[help-proc unknown-proc]) → any
name : (or/c string? path?)
argv : (or/c (listof string?) (vectorof string?))
table : (listof (cons/c symbol? list?))
finish-proc : (list? any/c ... . -> . any)
arg-help-strs : (listof string?)
help-proc : (string? . -> . any) = (lambda (str) ....)
unknown-proc : (string? . -> . any) = (lambda (str) ...)
table의 명세를 사용해 명령줄을 파싱합니다. 명령줄 파싱의 개요는 command-line 폼을 참고하세요. 그것이 대부분의 목적에 더 편리한 표기법을 제공합니다.
이 프로시저적 폼에 대한 table 인자는 args 절을 제외한, command-line의 절들의 정보를 인코딩합니다. 대신 인자들은 finish-proc 프로시저에 의해 처리되고, 비-플래그 인자에 대한 도움말 정보는 arg-help-strs에 제공됩니다. 또한 finish-proc 프로시저는 플래그를 파싱하는 동안 축적된 정보를 받습니다. help-proc와 unknown-proc 인자는 command-line으로는 불가능한 사용자 지정을 허용합니다.
더 이상 플래그가 없을 때, finish-proc는 명령줄 플래그에 대해 축적된 정보의 리스트(아래 참고)와 명령줄에서 남은 비-플래그 인자들과 함께 호출됩니다. finish-proc의 애리티는 명령줄에서 수용되고 요구되는 비-플래그 인자의 수를 결정합니다. 예를 들어, finish-proc가 두 개나 세 개의 인자를 수용한다면, 명령줄에는 한 개나 두 개의 비-플래그 인자가 제공되어야 합니다. finish-proc 프로시저는 0 또는 0들의 리스트(procedure-arity 참고)를 제외한 어떤 애리티도 가질 수 있습니다(즉, 프로시저는 적어도 한 개 이상의 인자를 수용해야 합니다).
arg-help-strs 인자는 예상되는 (비-플래그) 명령줄 인자를 식별하는 문자열들의 리스트로, 인자마다 하나씩입니다. 임의의 수의 인자가 허용되면, arg-help-strs의 마지막 문자열이 그것들 모두를 나타냅니다.
help-proc 프로시저는 -h 또는 --help 플래그가 명령줄에 포함되면 도움말 문자열과 함께 호출됩니다. 알 수 없는 플래그를 만나면, unknown-proc 프로시저가 플래그 처리 프로시저처럼(아래 설명) 호출됩니다; 그것은 적어도 한 개의 인자(알 수 없는 플래그)를 수용해야 하지만, 더 많은 인자도 수용할 수 있습니다. 기본 help-proc는 문자열을 표시하고 종료하며, 기본 unknown-proc는 exn:fail 예외를 일으킵니다.
table은 플래그 명세 집합들의 리스트입니다. 각 집합은 두 항목의 쌍으로 표현됩니다: 모드 심볼과, 도움말 문자열들 또는 플래그 명세들의 리스트. 모드 심볼은 'once-each, 'once-any, 'multi, 'final, 'help-labels, 'usage-help, 'ps 중 하나이며, command-line의 대응하는 절 태그와 같은 의미를 갖습니다. 'help-labels, 'usage-help, 'ps 모드에는 도움말 문자열 리스트가 제공됩니다. 다른 모드에는 플래그 명세 리스트가 제공되는데, 각 명세는 여러 플래그를 단일 핸들러 프로시저에 매핑합니다. 명세는 세 항목의 리스트입니다:
-
명세에 의해 정의된 플래그들을 위한 문자열들의 리스트. 플래그 문자열의 형식에 대한 정보는
command-line을 참고하세요. -
명령줄에서 플래그 중 하나가 발견될 때 그 플래그와 인자들을 처리하는 프로시저. 이 핸들러 프로시저의 애리티는 플래그가 소비하는 인자의 수를 결정합니다: 핸들러 프로시저는 핸들러 프로시저의 애리티와 맞도록 플래그 문자열과 명령줄에서 다음 몇 개의 인자들과 함께 호출됩니다. 핸들러 프로시저는 플래그를 받기 위해 적어도 한 개의 인자를 수용해야 합니다. 핸들러가 임의의 수의 인자를 수용한다면, 남은 모든 인자들이 핸들러에 전달됩니다. 핸들러 프로시저의 애리티는 숫자 또는 arity-at-least 값이어야 합니다.
핸들러의 반환 값은 결국
finish-proc에 전달되는 리스트에 추가됩니다. 핸들러가#<void>를 반환하면 이 리스트에 값이 추가되지 않습니다. 핸들러들이 반환한 모든#<void>가 아닌 값들에 대해, 리스트에서 값들의 순서는 명령줄에서 인자들의 순서와 같습니다. -
명세의 도움말 정보를 구성하기 위한 비어 있지 않은 리스트. 리스트의 첫 요소는 플래그를 설명합니다; 그것은 문자열이거나 비어 있지 않은 문자열들의 리스트일 수 있고, 후자의 경우 각 문자열은 자체 줄에 표시됩니다. 메인 리스트의 추가 요소들은 플래그의 예상 인자들을 이름 짓기 위해 문자열이어야 합니다. 명세를 위해 제공된 추가 도움말 문자열의 수는 명세의 핸들러 프로시저가 수용하는 인자의 수와 맞아야 합니다.
다음 예시는 command-line의 핵심 예시와 같은 것으로, 프로시저적 형태로 번역된 것입니다:
(parse-command-line "compile" (current-command-line-arguments)
`((once-each
[("-v" "--verbose")
,(lambda (flag) (verbose-mode #t))
("Compile with verbose messages")]
[("-p" "--profile")
,(lambda (flag) (profiling-on #t))
("Compile with profiling")])
(once-any
[("-o" "--optimize-1")
,(lambda (flag) (optimize-level 1))
("Compile with optimization level 1")]
[("--optimize-2")
,(lambda (flag) (optimize-level 2))
(("Compile with optimization level 2,"
"which implies all optimizations of level 1"))])
(multi
[("-l" "--link-flags")
,(lambda (flag lf) (link-flags (cons lf (link-flags))))
("Add a flag <lf> for the linker" "lf")]))
(lambda (flag-accum file) file)
'("filename"))
더 알아보기
- 매개변수 (Parameters) —
current-command-line-arguments - 시스템 경로 (System Paths) —
find-system-path - The Racket Guide: Command-Line Parsing