What's the best way to parse command line arguments?
解析python命令行参数最简单、最简洁、最灵活的方法或库是什么?
其他答案确实提到了对于新的python来说,
1)初始化
1 2 3 4 | import argparse # Instantiate the parser parser = argparse.ArgumentParser(description='Optional app description') |
2)添加参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 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)解析
1 | args = parser.parse_args() |
4)访问权限
1 2 3 4 5 | print("Argument values:") print(args.pos_arg) print(args.opt_pos_arg) print(args.opt_arg) print(args.switch) |
5)检查值
1 2 | if args.pos_arg > 10: parser.error("pos_arg cannot be larger than 10") |
用法
正确使用:
1 2 3 4 5 6 7 | $ ./app 1 2 --opt_arg 3 --switch Argument values: 1 2 3 True |
参数不正确:
1 2 3 4 5 6 7 8 9 10 11 12 | $ ./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 |
完全帮助:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ ./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 |
这个答案建议使用
正如其他人指出的那样,你最好是和optparse一起而不是getopt。getopt几乎是标准getopt(3)c库函数的一对一映射,而且不容易使用。
optparse虽然有点冗长,但它的结构要好得多,在以后的扩展中也更简单。
下面是一个典型的向解析器添加选项的行:
1 2 3 | parser.add_option('-q', '--query', action="store", dest="query", help="query string", default="spam") |
它基本上说明了它自己;在处理时,它将接受-q或--query作为选项,将参数存储在一个名为query的属性中,如果不指定该属性,则具有默认值。它也是自文档化的,因为您声明了帮助参数(与-h/--help一起运行时将使用该参数)和选项。
通常,您用以下方式分析您的参数:
1 | options, args = parser.parse_args() |
默认情况下,这将解析传递给脚本的标准参数(sys.argv[1:]
然后,options.query将被设置为传递给脚本的值。
只需通过执行
1 | parser = optparse.OptionParser() |
这些都是你需要的基础。下面是一个完整的python脚本,其中显示了:
1 2 3 4 5 6 7 8 9 10 11 | 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中,并使用
1 | python sample.py |
一次与
1 | python sample.py --query myquery |
除此之外,您将发现optparse非常容易扩展。在我的一个项目中,我创建了一个命令类,它允许您轻松地在命令树中嵌套子命令。它大量使用optparse将命令链接在一起。这不是我能用几行轻松解释的东西,但是在我的存储库中随意浏览主类,以及使用它的类和选项分析器。
使用DOCOPT
自2012年以来,python有一个非常简单、强大且非常酷的用于参数解析的模块,叫做docopt。它与Python2.6到3.5一起工作,不需要安装(只需复制它)。以下是从IT文档中获取的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | """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接口。以下是文档中的一个简单示例(这个小程序向命令行公开函数
1 2 3 4 5 6 7 8 9 | import fire class Calculator(object): def double(self, number): return 2 * number if __name__ == '__main__': fire.Fire(Calculator) |
在命令行中,可以运行:
1 2 3 4 | > calculator.py double 10 20 > calculator.py double --number=15 30 |
太棒了,不是吗?
由于这些原因,新的hip方式是
更新:自PY2.7起,argparse是标准库的一部分,optparse已弃用。
我更喜欢点击。它抽象了管理选项,并允许"(…)以一种可组合的方式创建漂亮的命令行接口,只需要很少的代码"。
以下是示例用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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() |
它还自动生成格式良好的帮助页面:
1 2 3 4 5 6 7 8 9 | $ 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. |
几乎每个人都在使用getopt
以下是文档的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
总之,这就是它的工作原理。
你有两种选择。接受争论的人就像开关一样。
这里描述的是简短的论点:
最后,
结果是一个夫妇列表(选项、参数)
如果一个选项不接受任何参数(比如这里的
我希望这对你有帮助。
使用标准库附带的
1 2 3 4 5 6 7 8 9 10 11 | #!/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?
轻型命令行参数默认值
虽然
1 2 3 4 5 6 7 | 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'参数捕获脚本名,未使用。测试输出如下:
1 2 3 4 5 6 | > ./test.py a 2 > ./test.py A A 2 > ./test.py A 20 A 20 |
对于我只需要一些默认值的简单脚本,我发现这已经足够了。您可能还希望在返回值中包含一些类型强制,或者命令行值都是字符串。
为了以防万一,如果需要在win32(2K、XP等)上获取unicode参数,这可能会有所帮助:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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() |
我认为大型项目的最佳方法是optparse,但是如果您正在寻找一种简单的方法,也许http://werkzeug.pocoo.org/documentation/script适合您。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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() |
所以基本上,每个函数操作都暴露在命令行中,帮助消息是免费生成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | python foo.py usage: foo.py [<options>] foo.py --help actions: bar: action bar does bar --id integer 0 --title string default title foo: action foo does foo --name string |
我更喜欢optparse而不是getopt。这是非常声明性的:你告诉它选项的名称和它们应该具有的效果(例如,设置一个布尔字段),然后它会返回一个根据你的规范填充的字典。
http://docs.python.org/lib/module-optparse.html
这里应该提到consoleargs。它很容易使用。过来看:
1 2 3 4 5 6 7 8 9 10 11 12 | 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() |
现在在控制台:
1 2 3 4 5 6 7 8 9 10 11 12 13 | % 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'' |
argparse代码不能长于实际的实现代码!
我发现,对于大多数流行的参数解析选项来说,这是一个问题,即如果您的参数只是适度的,那么用于记录它们的代码将变得非常大,无法满足它们所提供的好处。
一个相对新来的论点解析场景(我认为)是普莱西。
它使用argparse进行了一些公认的权衡,但使用了内联文档并简单地围绕
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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) |
我扩展了erco的方法,允许使用必需的位置参数和可选参数。这些应该在-d、-v等参数之前。
位置参数和可选参数可以分别用posarg(i)和optarg(i,默认)检索。当找到可选参数时,搜索选项(例如-i)的起始位置将向前移动1,以避免导致"意外"致命错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | import os,sys def HelpAndExit(): print("<<your help output goes here>>") sys.exit(1) def Fatal(msg): sys.stderr.write("%s: %s " % (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) |
这是一个方法,而不是一个图书馆,它似乎对我有用。
这里的目标是简明扼要,每一个参数都由一行来解析,args为可读性而排列,代码很简单,不依赖任何特殊模块(仅OS+sys),优雅地警告丢失或未知参数,使用简单的for/range()循环,并在python 2.x和3.x上工作。
显示了两个切换标志(-d,-v)和两个由参数控制的值(-i xxx和-o xxx)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import os,sys def HelpAndExit(): print("<<your help output goes here>>") sys.exit(1) def Fatal(msg): sys.stderr.write("%s: %s " % (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"跳过循环,将标志解析保持在一行中。