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

python command-line command-line-arguments

171463 观看

15回复

7790 作者的声誉

解析Python命令行参数的最简单简洁,最灵活的方法或库是什么?

作者: kamens 的来源 发布者: 2008 年 8 月 21 日

回应 15


14

10442 作者的声誉

Use optparse which comes with the standard library. For example:

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

Source: Using Python to create UNIX command line tools

However as of Python 2.7 optparse is deprecated, see: Why use argparse rather than optparse?

作者: Corey 发布者: 2008 年 8 月 21 日

15

9094 作者的声誉

几乎每个人都在使用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.argv几乎是你char** argv的C.在C中你跳过第一个元素,它是你的程序的名称,只解析参数:sys.argv[1:]

Getopt.getopt 会根据你在论证中给出的规则来解析它。

"ho:v" here describes the short arguments : -ONELETTER. The : means that -o accepts one argument.

Finally ["help", "output="] describes long arguments ( --MORETHANONELETTER ). The = after output once again means that output accepts one arguments.

The result is a list of couple (option,argument)

If an option doesn't accept any argument (like --help here) the arg part is an empty string. You then usually want to loop on this list and test the option name as in the example.

I hope this helped you.

作者: fulmicoton 发布者: 2008 年 8 月 21 日

6

10681 作者的声誉

Just in case you might need to, this may help if you need to grab unicode arguments on Win32 (2K, XP etc):


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()
作者: Shadow2531 发布者: 2008 年 8 月 21 日

3

40363 作者的声誉

我更喜欢optparse到getopt。这是非常具有说服力的:你告诉它选项的名称和它们应该具有的效果(例如,设置一个布尔字段),然后它会根据你的规范提供给你的字典。

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

作者: Chris Conway 发布者: 2008 年 8 月 21 日

165

30061 作者的声誉

决定

这个答案表明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将命令链接在一起。这不是我可以在几行中轻松解释的内容,但可以随意在我的存储库中浏览主类,以及使用它和选项解析器的类

作者: Thomas Vander Stichele 发布者: 2008 年 8 月 25 日

3

37174 作者的声誉

我认为大型项目的最佳方式是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
作者: Peter Hoffmann 发布者: 2008 年 8 月 27 日

36

6735 作者的声誉

新的臀部方式是argparse出于这些原因。argparse> optparse> getopt

更新:从py2.7开始,argparse是标准库的一部分,不推荐使用optparse

作者: Silfheed 发布者: 2009 年 6 月 11 日

1

9137 作者的声誉

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''
作者: stalk 发布者: 2012 年 6 月 30 日

66

2080 作者的声誉

使用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行代码加上你的doc字符串必不可少的,你可以在你的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
作者: ndemou 发布者: 2013 年 5 月 4 日

22

2100 作者的声誉

我更喜欢Click。它抽象了管理选项,并允许“(...)以可组合的方式创建漂亮的命令行界面,只需要很少的代码”。

以下是示例用法:

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.
作者: suda 发布者: 2014 年 11 月 21 日

185

19642 作者的声誉

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
作者: Andrzej Pronobis 发布者: 2015 年 5 月 27 日

0

187 作者的声誉

这是一个方法,而不是一个库,似乎对我有用。

这里的目标是简洁的,每个参数由一行解析,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'跳过循环,保持标记解析为一个衬里。

作者: erco 发布者: 2016 年 4 月 9 日

6

4008 作者的声誉

Lightweight command line argument defaults

Although argparse is great and is the right answer for fully documented command line switches and advanced features, you can use function argument defaults to handles straightforward positional arguments very simply.

import sys

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

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

The 'name' argument captures the script name and is not used. Test output looks like this:

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

For simple scripts where I just want some default values, I find this quite sufficient. You might also want to include some type coercion in the return values or command line values will all be strings.

作者: Simon Hibbs 发布者: 2017 年 4 月 5 日

2

889 作者的声誉

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)
作者: QA Collective 发布者: 2018 年 7 月 5 日

0

1 作者的声誉

我扩展了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) 
作者: Erik 发布者: 2018 年 7 月 24 日
32x32