명령 줄 인수를 구문 분석하는 가장 좋은 방법은 무엇입니까? [닫은]


251

무엇 쉬운 , tersest , 가장 유연한 파이썬 명령 줄 인수를 구문 분석 방법 또는 라이브러리는?

답변:


183

이 답변은 optparse이전 Python 버전에 적합한 것을 제안 합니다. Python 2.7 이상에서는을 argparse대체합니다 optparse. 자세한 내용은 이 답변 을 참조하십시오.

다른 사람들이 지적했듯이 getopt보다 optparse를 사용하는 것이 좋습니다. getopt는 표준 getopt (3) C 라이브러리 함수의 일대일 맵핑이며 사용하기 쉽지 않습니다.

optparse는 조금 더 장황하지만 나중에 더 확장하기가 훨씬 더 체계적이고 단순합니다.

파서에 옵션을 추가하는 일반적인 라인은 다음과 같습니다.

parser.add_option('-q', '--query',
            action="store", dest="query",
            help="query string", default="spam")

그것은 거의 스스로를 말합니다. 처리시 -q 또는 --query를 옵션으로 승인하고 인수를 query라는 속성에 저장하며, 지정하지 않으면 기본값을 갖습니다. 또한 옵션을 사용하여 도움말 인수 (-h /-help와 함께 실행할 때 사용됨)를 선언한다는 자체 문서화도 있습니다.

보통 당신은 다음과 같이 논증을 파싱합니다 :

options, args = parser.parse_args()

기본적으로 스크립트에 전달 된 표준 인수 (sys.argv [1 :])를 구문 분석합니다.

options.query는 스크립트에 전달한 값으로 설정됩니다.

당신은 단순히 수행하여 파서를 만듭니다

parser = optparse.OptionParser()

이것들은 당신이 필요로하는 모든 기본 사항입니다. 다음은이를 보여주는 완전한 Python 스크립트입니다.

import optparse

parser = optparse.OptionParser()

parser.add_option('-q', '--query',
    action="store", dest="query",
    help="query string", default="spam")

options, args = parser.parse_args()

print 'Query string:', options.query

5 줄의 파이썬으로 기본 사항을 보여줍니다.

sample.py에 저장하고 다음으로 한 번 실행하십시오.

python sample.py

그리고 한번

python sample.py --query myquery

그 외에도 optparse는 확장하기가 매우 쉽다는 것을 알게 될 것입니다. 내 프로젝트 중 하나에서 명령 트리에 하위 명령을 쉽게 중첩시킬 수있는 Command 클래스를 만들었습니다. optparse를 많이 사용하여 명령을 함께 연결합니다. 몇 줄로 쉽게 설명 할 수는 없지만 주 클래스의 저장소 와 클래스 및 옵션 파서를 사용하는 클래스를 자유롭게 탐색 하십시오.


9
이 답변은 파이썬 2.3에서 2.6까지 훌륭하고 명확하고 따르기 쉽습니다. 파이썬 2.7 이상에서는 argparse가 표준 라이브러리의 일부이며 optparse는 더 이상 사용되지 않으므로 가장 좋은 대답은 아닙니다.
matt wilkie

필자의 경우 속도 저하를 감지하기 위해 응용 프로그램을 프로파일 링하고 싶습니다. [a] ( tunahub.com/nschloe/tuna ) 라는 또 다른 도구가 있는데 단순히 agrs를 추가하여 전체 응용 프로그램을 프로파일 링 할 수 -mcProfile -o program.prof있지만 agrparcer는 args를 캡처하고 있습니다.이 args를 python exe에 전달하는 방법
Yogeshwar

231

argparse갈 길입니다. 사용 방법에 대한 간단한 요약은 다음과 같습니다.

1) 초기화

import argparse

# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')

2) 인수 추가

# Required positional argument
parser.add_argument('pos_arg', type=int,
                    help='A required integer positional argument')

# Optional positional argument
parser.add_argument('opt_pos_arg', type=int, nargs='?',
                    help='An optional integer positional argument')

# Optional argument
parser.add_argument('--opt_arg', type=int,
                    help='An optional integer argument')

# Switch
parser.add_argument('--switch', action='store_true',
                    help='A boolean switch')

3) 파싱

args = parser.parse_args()

4) 액세스

print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)

5) 값 확인

if args.pos_arg > 10:
    parser.error("pos_arg cannot be larger than 10")

용법

올바른 사용법 :

$ ./app 1 2 --opt_arg 3 --switch

Argument values:
1
2
3
True

잘못된 주장 :

$ ./app foo 2 --opt_arg 3 --switch
usage: convert [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
app: error: argument pos_arg: invalid int value: 'foo'

$ ./app 11 2 --opt_arg 3
Argument values:
11
2
3
False
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
convert: error: pos_arg cannot be larger than 10

전체 도움말 :

$ ./app -h

usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]

Optional app description

positional arguments:
  pos_arg            A required integer positional argument
  opt_pos_arg        An optional integer positional argument

optional arguments:
  -h, --help         show this help message and exit
  --opt_arg OPT_ARG  An optional integer argument
  --switch           A boolean switch

10
이것은 매우 간결하고 유용하며 여기에는 편의상 공식 문서가 있습니다. docs.python.org/3/library/argparse.html
Christophe Roussy

1
argparse가 너무 장황하다는 것을 발견하면 대신 plac를 사용하십시오.
Nimitz14

76

docopt 사용

2012 년부터 docopt 라는 인수 구문 분석을위한 매우 쉽고 강력하며 멋진 모듈이 있습니다. 다음은 문서에서 가져온 예입니다.

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)

이 그것입니다 그래서 : 코드의 2 개 라인 플러스 문서화 문자열 입니다 필수을하고 당신이 얻을 당신의 인수를 구문 분석하고 인수 객체에서 사용할 수 있습니다.

python-fire 사용

2017 년부터 python-fire 라는 또 다른 멋진 모듈이 있습니다. 그것은 당신이 일을 사용하여 코드에 대한 CLI 인터페이스를 생성 할 수 있습니다 제로 인수 구문 분석을. 다음은 설명서의 간단한 예입니다 (이 작은 프로그램은 double명령 줄에 기능 을 제공합니다 ).

import fire

class Calculator(object):

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

명령 행에서 다음을 실행할 수 있습니다.

> calculator.py double 10
20
> calculator.py double --number=15
30

4
docopt는 어떻게 "설치가 필요하지 않습니까"? 파이썬 모듈이므로 설치해야합니다. 'ImportError를 : 없음 모듈 이름 docopt 없다'
날카로운

1
@keen은 확실히 파이썬에 포함되어 있지는 않지만 설치할 필요는 없습니다 : "docopt.py 파일을 프로젝트에 드롭 할 수 있습니다 – 그것은 독립적입니다" -github.com/docopt/docopt
ndemou

9
우리는 단지 설치에 대한 다른 정의를 가지고 있습니다. 그리고 나는 미래 독자들을 위해 그것을 지적하고 싶었습니다.
keen

1
@keen 나는 당신의 정의를 공유하는 사람들을 위해 "설치 없음"에 대한 메모를 추가했습니다 :-)
ndemou

39

새로운 엉덩이 방법입니다 argparse위해 이러한 이유. argparse> optparse> getopt

업데이트 : py2.7부터 argparse 는 표준 라이브러리의 일부이며 optparse 는 더 이상 사용되지 않습니다.


귀하의 주요 링크는 404이므로 동일한 주제를 다루는 SO 질문에 대한 링크로 대체했습니다.
Joe Holloway

28

클릭을 선호합니다 . 옵션 관리를 추상화하고 "(...) 필요한만큼 적은 코드로 구성 가능한 방식으로 멋진 명령 줄 인터페이스를 만들 수 있습니다".

사용법 예는 다음과 같습니다.

import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
              help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()

또한 멋진 형식의 도움말 페이지를 자동으로 생성합니다.

$ python hello.py --help
Usage: hello.py [OPTIONS]

  Simple program that greets NAME for a total of COUNT times.

Options:
  --count INTEGER  Number of greetings.
  --name TEXT      The person to greet.
  --help           Show this message and exit.

14

거의 모든 사람들이 getopt를 사용하고 있습니다

이 문서의 예제 코드는 다음과 같습니다.

import getopt, sys

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)
    output = None
    verbose = False
    for o, a in opts:
        if o == "-v":
            verbose = True
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-o", "--output"):
            output = a

한마디로 여기에 그것이 작동하는 방법이 있습니다.

두 가지 유형의 옵션이 있습니다. 논쟁을 받고있는 사람들과 스위치와 같은 사람들.

sys.argv꽤 많은 당신을한다 char** argvC.처럼에서 당신은 당신의 프로그램 및 구문 분석만을 인수의 이름을 첫 번째 요소를 건너 C에서 :sys.argv[1:]

Getopt.getopt 당신이 주장하는 규칙에 따라 그것을 파싱 할 것입니다.

"ho:v"여기에 짧은 주장이 설명되어 -ONELETTER있습니다. :수단 -o하나 개의 인수를 받아들입니다.

마지막으로 ["help", "output="]긴 인수 ( --MORETHANONELETTER)를 설명 합니다. =후 출력은 다시 한 번 그 출력은 하나 개의 인수를 받아들이는 것을 의미한다.

결과는 커플 목록입니다 (옵션, 인수)

옵션이 --help여기 와 같은 인수를 허용하지 않으면 arg부분은 빈 문자열입니다. 그런 다음 일반적으로이 목록을 반복하고 예제와 같이 옵션 이름을 테스트하려고합니다.

이것이 도움이 되었기를 바랍니다.


6
getopt최신 버전의 Python에서 더 이상 사용되지 않으므로이 답변은 오래되었습니다.
shuttle87

1
@ shuttle87 python3.7.2부터는 getopt더 이상 사용되지 않습니다… 그러나 문서에 따르면 C getopt()함수에 익숙한 사용자에게 주로 제공되며 다른 사용자에게는 argparse더 적은 코드를 작성하고 얻을 수있는 더 나은 솔루션 일 수 있습니다 더 나은 도움말 및 오류 메시지 "
Skippy le Grand Gourou

14

사용 optparse표준 라이브러리와 함께 제공됩니다. 예를 들면 다음과 같습니다.

#!/usr/bin/env python
import optparse

def main():
  p = optparse.OptionParser()
  p.add_option('--person', '-p', default="world")
  options, arguments = p.parse_args()
  print 'Hello %s' % options.person

if __name__ == '__main__':
  main()

소스 : Python을 사용하여 UNIX 명령 행 도구 작성

그러나 Python 2.7부터 optparse는 더 이상 사용되지 않습니다. optparse 대신 argparse를 사용하는 이유는 무엇입니까?


6

Win32 (2K, XP 등)에서 유니 코드 인수 를 가져와야 하는 경우 도움이 될 수 있습니다 .


from ctypes import *

def wmain(argc, argv):
    print argc
    for i in argv:
        print i
    return 0

def startup():
    size = c_int()
    ptr = windll.shell32.CommandLineToArgvW(windll.kernel32.GetCommandLineW(), byref(size))
    ref = c_wchar_p * size.value
    raw = ref.from_address(ptr)
    args = [arg for arg in raw]
    windll.kernel32.LocalFree(ptr)
    exit(wmain(len(args), args))
startup()

감사합니다. 이 스크립트는 시작 명령을 GVim에 전달할 때 필요한 복잡한 인용문을 해결하는 데 도움이되었습니다.
telotortium

6

간단한 명령 행 인수 기본값

argparse완벽하게 문서화 된 명령 행 스위치 및 고급 기능에 대한 정답은 아니지만 , 함수 인수 기본값을 사용하여 간단한 위치 인수를 매우 간단하게 처리 할 수 ​​있습니다.

import sys

def get_args(name='default', first='a', second=2):
    return first, int(second)

first, second = get_args(*sys.argv)
print first, second

'name'인수는 스크립트 이름을 캡처하며 사용되지 않습니다. 테스트 결과는 다음과 같습니다.

> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20

기본값을 원하는 간단한 스크립트의 경우이 정도면 충분합니다. 리턴 값에 일부 유형 강제를 포함 시키거나 명령 행 값이 모두 문자열이 될 수도 있습니다.


2
def 문에서 인용 부호가 일치하지 않습니다.
historystamp


3

더 큰 프로젝트에 가장 적합한 방법은 optparse라고 생각하지만 쉬운 방법을 찾고 있다면 http://werkzeug.pocoo.org/documentation/script 가 적합합니다.

from werkzeug import script

# actions go here
def action_foo(name=""):
    """action foo does foo"""
    pass

def action_bar(id=0, title="default title"):
    """action bar does bar"""
    pass

if __name__ == '__main__':
    script.run()

따라서 기본적으로 모든 함수 action_ *는 명령 행에 노출되며 멋진 도움말 메시지가 무료로 생성됩니다.

python foo.py 
usage: foo.py <action> [<options>]
       foo.py --help

actions:
  bar:
    action bar does bar

    --id                          integer   0
    --title                       string    default title

  foo:
    action foo does foo

    --name                        string

자동 인수 생성을 사용하여 작은 패키지를 개발했습니다 declarative_parser. 물론, werkzeug와 함께 일하고 있다면을 유지하는 것이 좋습니다 werkzung.script. 어쨌든, 나는 그런 접근 방식의 열렬한 팬입니다.
krassowski

3

Argparse 코드는 실제 구현 코드보다 길 수 있습니다!

그것은 가장 인기있는 인수 구문 분석 옵션에서 찾은 문제입니다. 매개 변수가 적당하지 않으면 매개 변수를 문서화하는 코드가 제공하는 이익에 비례하여 크기가 커진다는 것입니다.

인수 파싱 장면에 대한 비교적 새로운 이민자 (제 생각에)는 plac입니다 .

그것은 argparse와 함께 인정 된 트레이드 오프를 만들지 만 인라인 문서를 사용하고 단순히 main()유형 함수 함수 를 둘러 쌉니다 .

def main(excel_file_path: "Path to input training file.",
     excel_sheet_name:"Name of the excel sheet containing training data including columns 'Label' and 'Description'.",
     existing_model_path: "Path to an existing model to refine."=None,
     batch_size_start: "The smallest size of any minibatch."=10.,
     batch_size_stop:  "The largest size of any minibatch."=250.,
     batch_size_step:  "The step for increase in minibatch size."=1.002,
     batch_test_steps: "Flag.  If True, show minibatch steps."=False):
"Train a Spacy (http://spacy.io/) text classification model with gold document and label data until the model nears convergence (LOSS < 0.5)."

    pass # Implementation code goes here!

if __name__ == '__main__':
    import plac; plac.call(main)

요점 :이 예제에서와 같이 plac를 가장 적게 사용하는 것은 3.x 함수 주석을 사용하기 때문에 Python 3.x에만 해당됩니다.
barny

1

consoleargs는 여기서 언급 할 가치가 있습니다. 사용하기 매우 쉽습니다. 확인 해봐:

from consoleargs import command

@command
def main(url, name=None):
  """
  :param url: Remote URL 
  :param name: File name
  """
  print """Downloading url '%r' into file '%r'""" % (url, name)

if __name__ == '__main__':
  main()

이제 콘솔에서 :

% python demo.py --help
Usage: demo.py URL [OPTIONS]

URL:    Remote URL 

Options:
    --name -n   File name

% python demo.py http://www.google.com/
Downloading url ''http://www.google.com/'' into file 'None'

% python demo.py http://www.google.com/ --name=index.html
Downloading url ''http://www.google.com/'' into file ''index.html''

나는 선언적 파서 에서 비슷한 접근 방식을 사용 했습니다. 문서의 인수 추론 (타이핑, docstrings, kwargs) 을 참조하십시오 . 주요 차이점 : python3, 타입 힌트, pip-installable.
krassowski

1
2012 년 마지막 커밋
Boris

0

여기 라이브러리가 아닌 방법이 있습니다.

여기서 목표는 간결하고, 각 인수는 한 줄로 구문 분석되고, 인수는 가독성을 위해 정렬되며, 코드는 간단하며 특수 모듈 (os + sys 만 해당)에 의존하지 않으며 누락되거나 알 수없는 인수에 대해 경고합니다 간단한 for / range () 루프를 사용하고 python 2.x 및 3.x에서 작동합니다.

두 개의 토글 플래그 (-d, -v)와 인수 (-i xxx 및 -o xxx)로 제어되는 두 값이 표시됩니다.

import os,sys

def HelpAndExit():
    print("<<your help output goes here>>")
    sys.exit(1)

def Fatal(msg):
    sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
    sys.exit(1)

def NextArg(i):
    '''Return the next command line argument (if there is one)'''
    if ((i+1) >= len(sys.argv)):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return(1, sys.argv[i+1])

### MAIN
if __name__=='__main__':

    verbose = 0
    debug   = 0
    infile  = "infile"
    outfile = "outfile"

    # Parse command line
    skip = 0
    for i in range(1, len(sys.argv)):
        if not skip:
            if   sys.argv[i][:2] == "-d": debug ^= 1
            elif sys.argv[i][:2] == "-v": verbose ^= 1
            elif sys.argv[i][:2] == "-i": (skip,infile)  = NextArg(i)
            elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
            elif sys.argv[i][:2] == "-h": HelpAndExit()
            elif sys.argv[i][:1] == "-":  Fatal("'%s' unknown argument" % sys.argv[i])
            else:                         Fatal("'%s' unexpected" % sys.argv[i])
        else: skip = 0

    print("%d,%d,%s,%s" % (debug,verbose,infile,outfile))

NextArg ()의 목표는 누락 된 데이터를 확인하는 동안 다음 인수를 반환하는 것이며 NextArg ()가 사용될 때 'skip'은 루프를 건너 뛰어 플래그 구문 분석을 한 라이너로 유지합니다.


0

필요한 위치 인수와 선택적 인수를 허용하도록 Erco의 접근 방식을 확장했습니다. 이들은 -d, -v 등의 인수 앞에 와야합니다.

위치 및 선택적 인수는 각각 PosArg (i) 및 OptArg (i, 기본값)를 사용하여 검색 할 수 있습니다. 선택적인 인수가 발견되면 옵션을 검색하는 시작 위치 (예 : -i)가 '예기치 않은'치명적인 원인이되지 않도록 1 앞으로 이동합니다.

import os,sys


def HelpAndExit():
    print("<<your help output goes here>>")
    sys.exit(1)

def Fatal(msg):
    sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
    sys.exit(1)

def NextArg(i):
    '''Return the next command line argument (if there is one)'''
    if ((i+1) >= len(sys.argv)):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return(1, sys.argv[i+1])

def PosArg(i):
    '''Return positional argument'''
    if i >= len(sys.argv):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return sys.argv[i]

def OptArg(i, default):
    '''Return optional argument (if there is one)'''
    if i >= len(sys.argv):
        Fatal("'%s' expected an argument" % sys.argv[i])
    if sys.argv[i][:1] != '-':
        return True, sys.argv[i]
    else:
        return False, default


### MAIN
if __name__=='__main__':

    verbose = 0
    debug   = 0
    infile  = "infile"
    outfile = "outfile"
    options_start = 3

    # --- Parse two positional parameters ---
    n1 = int(PosArg(1))
    n2 = int(PosArg(2))

    # --- Parse an optional parameters ---
    present, a3 = OptArg(3,50)
    n3 = int(a3)
    options_start += int(present)

    # --- Parse rest of command line ---
    skip = 0
    for i in range(options_start, len(sys.argv)):
        if not skip:
            if   sys.argv[i][:2] == "-d": debug ^= 1
            elif sys.argv[i][:2] == "-v": verbose ^= 1
            elif sys.argv[i][:2] == "-i": (skip,infile)  = NextArg(i)
            elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
            elif sys.argv[i][:2] == "-h": HelpAndExit()
            elif sys.argv[i][:1] == "-":  Fatal("'%s' unknown argument" % sys.argv[i])
            else:                         Fatal("'%s' unexpected" % sys.argv[i])
        else: skip = 0

    print("Number 1 = %d" % n1)
    print("Number 2 = %d" % n2)
    print("Number 3 = %d" % n3)
    print("Debug    = %d" % debug)
    print("verbose  = %d" % verbose)
    print("infile   = %s" % infile)
    print("outfile  = %s" % outfile) 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.