【问题标题】:What's the best way to parse command line arguments? [closed]解析命令行参数的最佳方法是什么? [关闭]
【发布时间】:2022-01-21 06:14:42
【问题描述】:

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

【问题讨论】:

    标签: python command-line command-line-arguments


    【解决方案1】:

    使用标准库附带的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?

    【讨论】:

      【解决方案2】:

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

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

      "ho:v" 这里描述了短参数:-ONELETTER: 表示-o 接受一个参数。

      最后["help", "output="] 描述了长参数(--MORETHANONELETTER)。 再次输出后的= 表示输出接受一个参数。

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

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

      希望对你有所帮助。

      【讨论】:

      • 随着较新版本的 Python 中 getopt 的弃用,这个答案已经过时了。
      • @shuttle87 截至 python3.7.2,getopt 仍然没有被弃用......但its documentation 声明它主要提供给熟悉 C 函数的用户 getopt() 函数,并承认对于其他users argparse 可能是一个更好的解决方案,允许“编写更少的代码并获得更好的帮助和错误消息”。
      【解决方案3】:

      以防万一,如果您需要在 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 时需要执行的一些非常复杂的引用。
      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        此答案建议 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 非常容易扩展。 在我的一个项目中,我创建了一个 Command 类,它允许您轻松地将子命令嵌套在命令树中。它大量使用 optparse 将命令链接在一起。这不是我能用几行轻松解释的东西,但请随时 browse around in my repository 主课,以及 a class that uses it and the option parser

        【讨论】:

        • 这个答案非常清晰且易于理解——适用于 python 2.3 到 2.6。对于 python 2.7+,这不是最好的答案,因为 argparse 现在是标准库的一部分并且不推荐使用 optparse。
        • 就我而言,我想分析我的应用程序以检测速度慢。还有另一个名为 [tuna] (github.com/nschloe/tuna) 的工具,它允许我通过简单地添加 agrs -mcProfile -o program.prof 来分析整个应用程序,但 agrparcer 正在捕获这些参数,我如何将这些参数传递给 python exe???
        【解决方案6】:

        我认为大型项目的最佳方法是 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。无论如何,我非常喜欢这种方法。
        【解决方案7】:

        出于these 的原因,新的时尚方式是argparse。 argparse > optparse > getopt

        更新:从 py2.7 开始,argparse 是标准库的一部分,optparse 已弃用。

        【讨论】:

        • 您的主链接是 404,因此我将其替换为指向同一主题的 SO 问题的链接。
        【解决方案8】:

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

        【讨论】:

        【解决方案9】:

        使用文档

        自 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行代码加上你的文档字符串,必不可少的,你的参数被解析并在你的参数对象中可用。

        使用 python-fire

        自 2017 年以来,还有另一个很酷的模块,称为 python-fire。它可以为您的代码生成一个 CLI 接口,让您进行 zero 参数解析。这是文档中的一个简单示例(这个小程序将函数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
        

        【讨论】:

        • docopt 如何“无需安装”?它是一个 python 模块,因此必须安装它。 'ImportError: 没有名为 docopt 的模块'
        • @keen 它肯定不包含在 python 中,但你不需要安装它:“你可以把 docopt.py 文件放到你的项目中——它是独立的”——@ 987654323@
        • 我们只是对安装有不同的定义——我想为未来的读者指出这一点。
        • @keen 我已经为分享您的定义的人添加了关于“不安装”的注释:-)
        【解决方案10】:

        我更喜欢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.
        

        【讨论】:

          【解决方案11】:

          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
          

          【讨论】:

          【解决方案12】:

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

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

          【讨论】:

            【解决方案13】:

            轻量级命令行参数默认值

            虽然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
            

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

            【讨论】:

            • def 语句中的引号不匹配。
            【解决方案14】:

            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 函数注释。
            【解决方案15】:

            我扩展了 Erco 的方法,允许使用必需的位置参数和可选参数。这些应该在 -d、-v 等参数之前。

            可以分别使用 PosArg(i) 和 OptArg(i, default) 检索位置和可选参数。 当找到一个可选参数时,搜索选项(例如 -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) 
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-01-25
              • 2010-10-04
              • 2013-12-19
              • 2010-09-17
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多