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

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

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

What’s the easiest, tersest, and most flexible method or library for parsing Python command line arguments?


回答 0

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

This answer suggests optparse which is appropriate for older Python versions. For Python 2.7 and above, argparse replaces optparse. See this answer for more information.

As other people pointed out, you are better off going with optparse over getopt. getopt is pretty much a one-to-one mapping of the standard getopt(3) C library functions, and not very easy to use.

optparse, while being a bit more verbose, is much better structured and simpler to extend later on.

Here’s a typical line to add an option to your parser:

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

It pretty much speaks for itself; at processing time, it will accept -q or –query as options, store the argument in an attribute called query and has a default value if you don’t specify it. It is also self-documenting in that you declare the help argument (which will be used when run with -h/–help) right there with the option.

Usually you parse your arguments with:

options, args = parser.parse_args()

This will, by default, parse the standard arguments passed to the script (sys.argv[1:])

options.query will then be set to the value you passed to the script.

You create a parser simply by doing

parser = optparse.OptionParser()

These are all the basics you need. Here’s a complete Python script that shows this:

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 lines of python that show you the basics.

Save it in sample.py, and run it once with

python sample.py

and once with

python sample.py --query myquery

Beyond that, you will find that optparse is very easy to extend. In one of my projects, I created a Command class which allows you to nest subcommands in a command tree easily. It uses optparse heavily to chain commands together. It’s not something I can easily explain in a few lines, but feel free to browse around in my repository for the main class, as well as a class that uses it and the option parser


回答 1

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

argparse is the way to go. Here is a short summary of how to use it:

1) Initialize

import argparse

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

2) Add Arguments

# 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) Parse

args = parser.parse_args()

4) Access

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

5) Check Values

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

Usage

Correct use:

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

Argument values:
1
2
3
True

Incorrect arguments:

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

Full help:

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

回答 2

使用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

Using docopt

Since 2012 there is a very easy, powerful and really cool module for argument parsing called docopt. Here is an example taken from its documentation:

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

So this is it: 2 lines of code plus your doc string which is essential and you get your arguments parsed and available in your arguments object.

Using python-fire

Since 2017 there’s another cool module called python-fire. It can generate a CLI interface for your code with you doing zero argument parsing. Here’s a simple example from the documentation (this small program exposes the function double to the command line):

import fire

class Calculator(object):

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

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

From the command line, you can run:

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

回答 3

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

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

The new hip way is argparse for these reasons. argparse > optparse > getopt

update: As of py2.7 argparse is part of the standard library and optparse is deprecated.


回答 4

我更喜欢点击。它抽象化了管理选项,并允许“(…)以可组合的方式用所需的最少代码创建漂亮的命令行界面”。

这是示例用法:

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.

I prefer Click. It abstracts managing options and allows “(…) creating beautiful command line interfaces in a composable way with as little code as necessary”.

Here’s example usage:

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

It also automatically generates nicely formatted help pages:

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

回答 5

几乎每个人都在使用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部分为空字符串。然后,您通常希望在此列表上循环并测试示例中的选项名称。

希望对您有所帮助。

Pretty much everybody is using getopt

Here is the example code for the 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

So in a word, here is how it works.

You’ve got two types of options. Those who are receiving arguments, and those who are just like switches.

sys.argv is pretty much your char** argv in C. Like in C you skip the first element which is the name of your program and parse only the arguments : sys.argv[1:]

Getopt.getopt will parse it according to the rule you give in argument.

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


回答 6

使用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,请参阅:为什么使用argparse而不是optparse?

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?


回答 7

万一您可能需要,如果您需要在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()

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

回答 8

轻量级命令行参数默认值

尽管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

对于只需要一些默认值的简单脚本,我发现这已经足够了。您可能还希望在返回值中包含某种类型的强制,否则命令行值将全部为字符串。

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.


回答 9

我更喜欢optparse而不是getopt。这是非常具有说明性的:您告诉它选项的名称以及它们应具有的效果(例如,设置布尔字段),它会根据您的规范将您交给的字典交给您。

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

I prefer optparse to getopt. It’s very declarative: you tell it the names of the options and the effects they should have (e.g., setting a boolean field), and it hands you back a dictionary populated according to your specifications.

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


回答 10

我认为大型项目的最佳方法是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

I think the best way for larger projects is optparse, but if you are looking for an easy way, maybe http://werkzeug.pocoo.org/documentation/script is something for you.

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

So basically every function action_* is exposed to the command line and a nice help message is generated for free.

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

回答 11

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)

Argparse code can be longer than actual implementation code!

That’s a problem I find with most popular argument parsing options is that if your parameters are only modest, the code to document them becomes disproportionately large to the benefit they provide.

A relative new-comer to the argument parsing scene (I think) is plac.

It makes some acknowledged trade-offs with argparse, but uses inline documentation and wraps simply around main() type function function:

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)

回答 12

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

consoleargs deserves to be mentioned here. It is very easy to use. Check it out:

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

Now in console:

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

回答 13

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

这里的目标是简洁的,每个参数都由一行解析,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”跳过循环,从而将标志解析到一个衬里。

Here’s a method, not a library, which seems to work for me.

The goals here are to be terse, each argument parsed by a single line, the args line up for readability, the code is simple and doesn’t depend on any special modules (only os + sys), warns about missing or unknown arguments gracefully, use a simple for/range() loop, and works across python 2.x and 3.x

Shown are two toggle flags (-d, -v), and two values controlled by arguments (-i xxx and -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))

The goal of NextArg() is to return the next argument while checking for missing data, and ‘skip’ skips the loop when NextArg() is used, keeping the flag parsing down to one liners.


回答 14

我扩展了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) 

I extended Erco’s approach to allow for required positional arguments and for optional arguments. These should precede the -d, -v etc. arguments.

Positional and optional arguments can be retrieved with PosArg(i) and OptArg(i, default) respectively. When an optional argument is found the start position of searching for options (e.g. -i) is moved 1 ahead to avoid causing an ‘unexpected’ fatal.

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)