代码之家  ›  专栏  ›  技术社区  ›  kamens

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

  •  184
  • kamens  · 技术社区  · 16 年前

    什么是 最容易的 , 泰斯特 和大多数 灵活的 用于分析python命令行参数的方法或库?

    15 回复  |  直到 6 年前
        1
  •  154
  •   Community Stefan Steinegger    7 年前

    这个答案表明 optparse 它适用于旧的Python版本。对于python 2.7及更高版本, argparse 替换 optPARSE . 见 this answer 更多信息。

    正如其他人指出的那样,你最好是和optparse一起而不是getopt。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行python,向您展示基础知识。

    将其保存在sample.py中,并使用

    python sample.py
    

    一次与

    python sample.py --query myquery
    

    除此之外,您将发现optparse非常容易扩展。 在我的一个项目中,我创建了一个命令类,它允许您轻松地在命令树中嵌套子命令。它大量使用optparse将命令链接在一起。这不是我能用几句话轻易解释的事情,但请放心 browse around in my repository 对于主班,以及 a class that uses it and the option parser

        2
  •  146
  •   Andrzej Pronobis    9 年前

    其他答案确实提到了 argparse 是使用新的python的方法,但不要给出使用示例。为了完整起见,下面简要总结了如何使用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
    
        3
  •  61
  •   ndemou    7 年前

    使用DOCOPT

    自2012年以来,python拥有非常简单、强大和 凉爽的 调用了用于参数分析的模块 docopt . 它与Python2.6到3.5一起工作,不需要安装(只需复制它)。以下是从IT文档中获取的示例:

    """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行代码加上您的文档字符串 基本的,您可以在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
  •  36
  •   matt wilkie    13 年前

    新的时髦方式是 argparse 对于 these 原因。argparse>optparse>获取选项

    更新: PY2.7 argparse 是标准库的一部分,并且 optparse 被贬低。

        5
  •  15
  •   shuttle87 Bhargav Boda    10 年前

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

    Getopt.getopt 将根据您在参数中给出的规则分析它。

    "ho:v" 下面介绍简短的参数: -ONELETTER . 这个 : 意味着 -o 接受一个参数。

    终于 ["help", "output="] 描述长参数( --MORETHANONELETTER ) 这个 = 再次输出后意味着输出接受一个参数。

    结果是一个夫妇列表(选项、参数)

    如果选项不接受任何参数(如 --help 这里) arg 部分是空字符串。 然后,您通常希望在此列表上循环,并像示例中那样测试选项名。

    我希望这对你有帮助。

        6
  •  15
  •   Mark Butler    9 年前

    我更喜欢 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.
    
        7
  •  14
  •   Community Stefan Steinegger    7 年前

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

    来源: Using Python to create UNIX command line tools

    但是,从python 2.7开始,optparse已被弃用,请参见: Why use argparse rather than optparse?

        8
  •  6
  •   Shadow2531    16 年前

    以防万一,如果你需要的话,这可能会有所帮助。 抓住 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()
    
        9
  •  6
  •   Nick Cox Hans Z.    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
    

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

        10
  •  4
  •   Srikanth    16 年前

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

    所以基本上,每个函数操作都暴露在命令行中, 帮助消息是免费生成的。

    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
  •  3
  •   Charles Duffy    16 年前

    我更喜欢optparse而不是getopt。它是非常声明性的:你告诉它选项的名称和它们应该具有的效果(例如,设置一个布尔字段),然后它会返回一个根据你的规范填充的字典。

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

        12
  •  2
  •   stalk    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''
    
        13
  •  1
  •   QA Collective    6 年前

    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)
    
        14
  •  0
  •   erco    8 年前

    这是一个方法,而不是一个图书馆,它似乎对我有用。

    这里的目标是简明扼要,每一个参数都由一行来解析,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”跳过循环,将标志解析保持在一行中。

        15
  •  0
  •   Erik    6 年前

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