解析命令行参数的最佳方法是什么?[关闭]


Answers:


183

该答案表明optparse哪种方法适用于较旧的Python版本。对于Python 2.7及更高版本,请argparse替换optparse。有关更多信息,请参见此答案

正如其他人指出的那样,您最好使用optparse而不是getopt。getopt几乎是标准getopt(3)C库函数的一对一映射,并且使用起来不是很容易。

optparse较为冗长,但结构更好,以后也更易于扩展。

这是向解析器添加选项的典型行:

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

这几乎可以说明一切。在处理时,它将接受-q或--query作为选项,将参数存储在名为query的属性中,如果未指定,则具有默认值。它也是自记录的,您可以在该选项的附近声明help参数(与-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行Python,向您展示基础知识。

将其保存在sample.py中,然后运行一次

python sample.py

然后一次

python sample.py --query myquery

除此之外,您还会发现optparse非常容易扩展。在我的一个项目中,我创建了一个Command类,该类使您可以轻松地将子命令嵌套在命令树中。它大量使用optparse将命令链接在一起。这不是我可以轻松地解释的内容,但是可以在我的存储库中随意浏览主类以及使用它的类和选项解析器


9
这个答案非常清楚并且易于理解-适用于python 2.3至2.6。对于python 2.7+,这不是最佳答案,因为argparse现在已成为标准库的一部分,并且optparse已弃用。
马特·威尔基

就我而言,我想分析我的应用程序以检测运行缓慢。还有另一个叫做[tuna]的工具(github.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)

因此,这是它:两行代码加上您的文档字符串,它必不可少的,你会得到你的参数解析,并在你的论点对象可用。

使用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如何“不需要安装”?这是一个python模块,因此必须安装。'ImportError:没有名为docopt的模块'
敏锐,

1
@keen肯定它没有包含在python中,但是您不需要安装它:“您可以将docopt.py文件放到您的项目中-它是自包含的” -github.com/docopt/docopt
ndemou

9
我们对安装有不同的定义-我想为以后的读者指出这一点。
敏锐

1
@keen我为共享您的定义的人添加了“未安装”的注释:-)
ndemou '16

39

新的髋关节的方法是argparse这些原因。argparse> optparse> getopt

更新:自py2.7起,argparse已成为标准库的一部分,而optparse已弃用。


您的主要链接是404,所以我用一个指向同一主题的SO问题的链接代替了它。
乔·霍洛威

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

这是doc的示例代码:

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.argvchar** argv在C中几乎就是您。与C中一样,您跳过第一个元素(它是程序的名称),并且仅解析参数:sys.argv[1:]

Getopt.getopt 将根据您在参数中给出的规则对其进行解析。

"ho:v"这里描述简短的论点:-ONELETTER。接受一个论点的:手段-o

最后["help", "output="]介绍长参数(--MORETHANONELETTER)。在=后输出再次意味着输出接受一个参数。

结果是一对夫妇(选项,参数)的列表

如果选项不接受任何参数(例如--help此处),则该arg部分为空字符串。然后,您通常希望在此列表上循环并测试示例中的选项名称。

希望对您有所帮助。


6
随着getopt新版本Python 的弃用,这个答案已经过时了。
穿梭车

1
@ shuttle87截至python3.7.2为止,getopt仍未弃用……但其文档指出,它主要是为熟悉C getopt()函数的用户提供的,并承认对于其他用户来说argparse可能是更好的解决方案,允许“编写更少的代码并获得更好的帮助和错误消息”。
Skippy le Grand Gourou


6

万一您可能需要,如果您需要在Win32(2K,XP等)上获取 unicode参数,这可能会有所帮助:


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的最新用途(如示例中所示)仅适用于Python 3.x,因为它使用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''

我在declarative-parser中使用了类似的方法,请参阅文档中的参数推导(类型,文档字符串,kwargs)。主要区别:python3,类型提示,可点子安装。
krassowski

1
上一次提交时间为2012年
鲍里斯(Boris)

0

这是一种对我有用的方法,而不是库。

这里的目标是简洁的,每个参数都由一行解析,args排列以提高可读性,代码简单且不依赖任何特殊模块(仅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) 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.