Cách tốt nhất để phân tích các đối số dòng lệnh là gì? [đóng cửa]


251

Là gì đơn giản nhất , tersest , và hầu hết các linh hoạt phương pháp hoặc thư viện để phân tích Python đối số dòng lệnh?

Câu trả lời:


183

Câu trả lời này cho thấy optparsecái nào phù hợp với các phiên bản Python cũ hơn. Đối với Python 2.7 trở lên, argparsethay thế optparse. Xem câu trả lời này để biết thêm thông tin.

Như những người khác đã chỉ ra, bạn nên đi với optparse hơn getopt. getopt gần như là ánh xạ một-một của các hàm thư viện C chuẩn (3) C, và không dễ sử dụng.

optparse, trong khi dài dòng hơn một chút, có cấu trúc tốt hơn và đơn giản hơn để mở rộng sau này.

Đây là một dòng điển hình để thêm một tùy chọn cho trình phân tích cú pháp của bạn:

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

Nó nói khá nhiều cho chính nó; tại thời điểm xử lý, nó sẽ chấp nhận -q hoặc --query làm tùy chọn, lưu trữ đối số trong một thuộc tính được gọi là truy vấn và có giá trị mặc định nếu bạn không chỉ định nó. Nó cũng tự ghi lại rằng bạn khai báo đối số trợ giúp (sẽ được sử dụng khi chạy với -h / - help) ngay tại đó với tùy chọn.

Thông thường bạn phân tích các đối số của bạn với:

options, args = parser.parse_args()

Theo mặc định, điều này sẽ phân tích các đối số chuẩn được truyền cho tập lệnh (sys.argv [1:])

Options.query sau đó sẽ được đặt thành giá trị bạn chuyển cho tập lệnh.

Bạn tạo một trình phân tích cú pháp đơn giản bằng cách thực hiện

parser = optparse.OptionParser()

Đây là tất cả những điều cơ bản bạn cần. Đây là một kịch bản Python hoàn chỉnh cho thấy điều này:

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 dòng trăn cho bạn thấy những điều cơ bản.

Lưu nó trong sample.py và chạy nó một lần với

python sample.py

và một lần với

python sample.py --query myquery

Ngoài ra, bạn sẽ thấy rằng optparse rất dễ mở rộng. Trong một trong các dự án của tôi, tôi đã tạo một lớp Command cho phép bạn lồng các lệnh con trong cây lệnh một cách dễ dàng. Nó sử dụng optparse rất nhiều để xâu chuỗi các lệnh lại với nhau. Đó không phải là thứ tôi có thể dễ dàng giải thích trong một vài dòng, nhưng hãy thoải mái duyệt xung quanh trong kho lưu trữ của tôi cho lớp chính, cũng như một lớp sử dụng nó và trình phân tích cú pháp tùy chọn


9
Câu trả lời này rất rõ ràng và dễ làm theo - đối với python 2.3 đến 2.6. Đối với python 2.7+, đây không phải là câu trả lời hay nhất vì argparse hiện là một phần của thư viện chuẩn và optparse không dùng nữa.
matt wilkie

Trong trường hợp của tôi, tôi muốn lập hồ sơ ứng dụng của mình để phát hiện sự chậm chạp. Có một công cụ khác gọi là [cá ngừ] ( github.com/nschloe/tuna ) cho phép tôi lập hồ sơ toàn bộ ứng dụng bằng cách thêm agrs -mcProfile -o program.profnhưng agrparcer đang nắm bắt các đối số này, làm cách nào để chuyển các đối số này sang python exe ???
Yogeshwar

231

argparselà con đường để đi Dưới đây là một bản tóm tắt ngắn về cách sử dụng nó:

1) Khởi tạo

import argparse

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

2) Thêm đối số

# 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) phân tích

args = parser.parse_args()

4) Truy cập

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

5) Kiểm tra giá trị

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

Sử dụng

Sử dụng đúng:

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

Argument values:
1
2
3
True

Đối số không chính xác:

$ ./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

Trợ giúp đầy đủ:

$ ./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
Điều này rất súc tích và hữu ích và đây là tài liệu chính thức (để thuận tiện): docs.python.org/3/l Library /argparse.html
Barshe Roussy

1
Nếu bạn thấy argparse quá dài dòng hãy sử dụng plac thay thế.
Nimitz14

76

Sử dụng docopt

Từ năm 2012, có một mô-đun rất dễ dàng, mạnh mẽ và thực sự tuyệt vời để phân tích cú pháp đối số được gọi là docopt . Dưới đây là một ví dụ được lấy từ tài liệu của nó:

"""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)

Vì vậy, đây là: 2 dòng mã cộng với chuỗi doc của bạn là điều cần thiết và bạn nhận được các đối số của mình được phân tích cú pháp và có sẵn trong đối tượng đối số của bạn.

Sử dụng lửa trăn

Kể từ năm 2017, có một mô-đun thú vị khác gọi là python-fire . Nó có thể tạo giao diện CLI cho mã của bạn khi bạn thực hiện phân tích đối số bằng không . Đây là một ví dụ đơn giản từ tài liệu (chương trình nhỏ này hiển thị hàm doublecho dòng lệnh):

import fire

class Calculator(object):

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

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

Từ dòng lệnh, bạn có thể chạy:

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

4
Làm thế nào để docopt "không cần cài đặt"? Đây là một mô-đun python nên nó phải được cài đặt. 'ImportError: Không có mô-đun tên docopt'
quan tâm

1
@keen chắc chắn nó không được bao gồm với python nhưng bạn không cần phải cài đặt nó: "bạn chỉ có thể thả tệp docopt.py vào dự án của mình - nó độc lập" - github.com/docopt/docopt
ndemou

9
chúng tôi chỉ có các định nghĩa khác nhau về cài đặt - và tôi muốn chỉ ra điều đó cho các độc giả trong tương lai.
quan tâm

1
@keen Tôi đã thêm một lưu ý về "không cài đặt" cho những người chia sẻ định nghĩa của bạn :-)
ndemou 21/07/2016

39

Cách hông mới là argparsenhững lý do này . argparse> optparse> getopt

cập nhật: Kể từ py2.7 argparse là một phần của thư viện chuẩn và optparse không được dùng nữa.


Liên kết chính của bạn là 404 vì vậy tôi đã thay thế nó bằng một liên kết đến một câu hỏi SO có cùng chủ đề.
Joe Holloway

28

Tôi thích Click . Nó trừu tượng hóa các tùy chọn quản lý và cho phép "(...) tạo ra các giao diện dòng lệnh đẹp theo cách có thể kết hợp với càng ít mã càng cần thiết".

Đây là ví dụ sử dụng:

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()

Nó cũng tự động tạo các trang trợ giúp được định dạng độc đáo:

$ 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

Khá nhiều người đang sử dụng getopt

Đây là mã ví dụ cho tài liệu:

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

Vì vậy, trong một từ, đây là cách nó hoạt động.

Bạn có hai loại tùy chọn. Những người đang nhận được tranh luận, và những người giống như các thiết bị chuyển mạch.

sys.argvlà khá nhiều char** argvtrong C. Giống như trong C, bạn bỏ qua phần tử đầu tiên là tên chương trình của bạn và chỉ phân tích các đối số:sys.argv[1:]

Getopt.getopt sẽ phân tích cú pháp theo quy tắc bạn đưa ra trong đối số.

"ho:v"ở đây mô tả các đối số ngắn : -ONELETTER. Có :nghĩa là -ochấp nhận một đối số.

Cuối cùng ["help", "output="]mô tả các đối số dài ( --MORETHANONELETTER). Đầu =ra sau một lần nữa có nghĩa là đầu ra chấp nhận một đối số.

Kết quả là một danh sách các cặp vợ chồng (tùy chọn, đối số)

Nếu một tùy chọn không chấp nhận bất kỳ đối số (như --helpở đây) thìarg phần đó là một chuỗi rỗng. Sau đó, bạn thường muốn lặp trong danh sách này và kiểm tra tên tùy chọn như trong ví dụ.

Tôi hy vọng điều này đã giúp bạn.


6
Với sự phản đối getopttrong các phiên bản mới hơn của Python, câu trả lời này đã lỗi thời.
đưa đón87

1
@ Shuttle87 Kể từ python3.7.2, getoptvẫn không bị từ chối Nhưng Nhưng tài liệu của nó nói rằng nó chủ yếu được cung cấp cho người dùng quen với getopt()chức năng C và thừa nhận rằng đối với những người dùng khác argparsecó thể là một giải pháp tốt hơn, cho phép "viết ít mã hơn và nhận được trợ giúp tốt hơn và thông báo lỗi ".
Skippy le Grand Gourou

14

Sử dụng optparseđi kèm với thư viện tiêu chuẩn. Ví dụ:

#!/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()

Nguồn: Sử dụng Python để tạo các công cụ dòng lệnh UNIX

Tuy nhiên, đối với Python 2.7 optparse không được dùng nữa, hãy xem: Tại sao nên sử dụng argparse thay vì optparse?


6

Chỉ trong trường hợp bạn có thể cần, điều này có thể hữu ích nếu bạn cần lấy các đối số unicode trên Win32 (2K, XP, v.v.):


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()

Cảm ơn bạn. Kịch bản này đã giúp tôi tìm ra một số trích dẫn thực sự phức tạp mà tôi cần phải làm khi chuyển các lệnh khởi động cho GVim.
telotortium

6

Đối số dòng lệnh nhẹ mặc định

Mặc dù argparselà tuyệt vời và là câu trả lời đúng cho các chuyển đổi dòng lệnh và các tính năng nâng cao được ghi lại đầy đủ, bạn có thể sử dụng mặc định đối số chức năng để xử lý các đối số vị trí đơn giản rất đơn giản.

import sys

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

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

Đối số 'name' ghi lại tên tập lệnh và không được sử dụng. Đầu ra thử nghiệm trông như thế này:

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

Đối với các tập lệnh đơn giản mà tôi chỉ muốn một số giá trị mặc định, tôi thấy điều này khá đủ. Bạn cũng có thể muốn bao gồm một số kiểu ép buộc trong các giá trị trả về hoặc giá trị dòng lệnh sẽ là các chuỗi.


2
các trích dẫn không khớp trong tuyên bố def.
historystamp

3

Tôi thích optparse để có được. Nó rất khai báo: bạn cho nó biết tên của các tùy chọn và hiệu ứng mà chúng nên có (ví dụ: đặt trường boolean) và nó đưa lại cho bạn một từ điển được điền theo thông số kỹ thuật của bạn.

http://docs.python.org/lib/module-optparse.html


3

Tôi nghĩ rằng cách tốt nhất cho các dự án lớn hơn là optparse, nhưng nếu bạn đang tìm kiếm một cách dễ dàng, có thể http://werkzeug.pocoo.org/documentation/script là thứ dành cho bạn.

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()

Vì vậy, về cơ bản mọi chức năng action_ * được hiển thị với dòng lệnh và một thông báo trợ giúp đẹp được tạo miễn phí.

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

Tôi đã phát triển một gói nhỏ sử dụng tạo đối số tự động : declarative_parser. Tất nhiên, nếu một người đang làm việc với werkzeug, có thể tốt hơn để giữ lại werkzung.script. Dù sao, tôi là một fan hâm mộ lớn của cách tiếp cận như vậy.
krassowski

3

Mã argparse có thể dài hơn mã thực hiện thực tế!

Đó là một vấn đề tôi gặp phải với hầu hết các tùy chọn phân tích đối số phổ biến là nếu các tham số của bạn chỉ khiêm tốn, mã để ghi lại chúng trở nên lớn không tương xứng với lợi ích mà chúng cung cấp.

Một người mới tương đối với cảnh phân tích đối số (tôi nghĩ) là plac .

Nó làm cho một số sự đánh đổi được thừa nhận với argparse, nhưng sử dụng tài liệu nội tuyến và kết thúc đơn giản xung quanh main()chức năng chức năng loại:

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)

Điểm thông tin: việc sử dụng plac gọn gàng nhất (như trong ví dụ) chỉ dành cho Python 3.x, vì nó sử dụng các chú thích hàm 3.x.
barny

1

an ủi xứng đáng được đề cập ở đây. Nó rất dễ sử dụng. Kiểm tra xem nó:

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()

Bây giờ trong giao diện điều khiển:

% 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''

Tôi đã sử dụng cách tiếp cận tương tự trong trình phân tích cú pháp khai báo , xem khấu trừ đối số (gõ, tài liệu , kwargs) trong tài liệu. Sự khác biệt chính: python3, gợi ý loại, có thể cài đặt pip.
krassowski

1
Cam kết cuối cùng vào năm 2012
Boris

0

Đây là một phương pháp, không phải là một thư viện, có vẻ như phù hợp với tôi.

Các mục tiêu ở đây là ngắn gọn, mỗi đối số được phân tích cú pháp bởi một dòng duy nhất, dòng đối số để dễ đọc, mã đơn giản và không phụ thuộc vào bất kỳ mô-đun đặc biệt nào (chỉ os + sys), cảnh báo về các đối số bị thiếu hoặc chưa biết một cách duyên dáng , sử dụng vòng lặp for / Range () đơn giản và hoạt động trên python 2.x và 3.x

Hiển thị là hai cờ chuyển đổi (-d, -v) và hai giá trị được điều khiển bởi các đối số (-i xxx và -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))

Mục tiêu của NextArg () là trả về đối số tiếp theo trong khi kiểm tra dữ liệu bị thiếu và 'bỏ qua' bỏ qua vòng lặp khi NextArg () được sử dụng, giữ cho cờ phân tích cú pháp xuống một lớp lót.


0

Tôi đã mở rộng cách tiếp cận của Erco để cho phép các đối số vị trí cần thiết và cho các đối số tùy chọn. Chúng nên đi trước các đối số -d, -v, v.v.

Các đối số tùy chọn và tùy chọn có thể được truy xuất với PosArg (i) và OptArg (i, mặc định) tương ứng. Khi tìm thấy một đối số tùy chọn, vị trí bắt đầu tìm kiếm các tùy chọn (ví dụ -i) được di chuyển 1 về phía trước để tránh gây tử vong 'bất ngờ'.

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) 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.