如何读取和处理命令行参数?

799

6
使用 docopt(参见 @ralbatross 在 https://dev59.com/hXNA5IYBdhLWcg3wVcFx#14790373 中的回答)。我已经尝试了其他所有方法,但实际上 docopt 是我今后唯一会使用的方法。 - Pat
3
我认为没有一种单一的最佳方法。argparse是标准且功能丰富的。docopt非常优雅,但不在标准库中。对于非常简单的轻量级使用,您可以使函数默认值处理命令行参数默认值 - Simon Hibbs
22个回答

687
import sys

print("\n".join(sys.argv))

sys.argv 是一个列表,其中包含传递给命令行脚本的所有参数。 sys.argv[0] 表示脚本名称。

基本上,

import sys
print(sys.argv[1:])

100
对于非常简单的内容,这是一种不错的方法。不过你可能只想使用 sys.argv[1:] (避免使用脚本名称)。 - Xiong Chiamiov

608

标准库中的规范解决方案是使用argparse文档):

以下是一个例子:

from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
                    help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
                    action="store_false", dest="verbose", default=True,
                    help="don't print status messages to stdout")

args = parser.parse_args()

argparse 支持(除其他功能外):

  • 任意顺序的多个选项。
  • 短选项和长选项。
  • 默认值。
  • 生成使用帮助信息。

35
是的,它们是最好的工具。因为它们是标准库的一部分,你可以确信它们会被提供并且使用起来也很容易。特别是 optparse 工具功能强大而且简单易用。 - Barry Wark
6
optparse 是最好的之一;而 getopt 已经老旧,应该被视为已过时。 - jemfinch
14
目前(2011年12月),argparse 被认为是比 optparse 更好的选择,对吗? - oob
63
Python文档建议使用argparse代替optparse。 - earthmeLon
10
由于optparse已被弃用,问题的提问者不再是Stack Overflow的成员,并且这是一个高度可见的问题的接受答案 - 请考虑完全重写您的示例代码以使用stdlib argparse代替。 - wim
显示剩余3条评论

132

我正在宣传 argparse,相比之下更适合以下情况:这些原因,基本上是:

  • argparse模块可以处理位置和可选参数,而optparse只能处理可选参数

  • argparse不会死板地约束你的命令行界面应该是什么样子——支持像-file或/file这样的选项,以及必需选项。Optparse拒绝支持这些功能,更喜欢纯洁而非实用的方式。

  • argparse生成更多相关的使用消息,包括从您的参数确定的命令行使用和帮助信息,适用于位置和可选参数。optparse模块要求您编写自己的使用字符串,并且没有显示位置参数帮助的方法。

  • argparse支持消耗变量数量的操作,而optparse需要预先知道精确的参数数量(例如1,2或3)

  • argparse支持分派到子命令的解析器,而optparse需要设置allow_interspersed_args并手动进行解析器分派

我的个人最爱:

  • argparse允许使用简单的可调用函数来指定type和action参数,而optparse需要修改类属性,如STORE_ACTIONS或CHECK_METHODS,以获得正确的参数检查

30
这现在已经成为标准Python的一部分,自2.7和3.2版本开始 :) - jpswain
2
“可选参数”是什么?你说它们在optparse中。我认为它们是可能提供或不提供的参数,但你说它们在optparse中,同时又说“optparse要求预先知道确切数量的参数”。所以,要么你对“可选参数”的定义与我想的不同,要么你的回答与自己不一致。 - ArtOfWarfare
4
只是一个抱怨:argparse文档也异常的复杂。你找不到一个简单的答案来回答“如何使命令行参数只接受一个值,以及如何访问该值”的问题。 - osman
6
@osman 这篇关于 argparse 的简明教程可能会有所帮助... - lifebalance
4
在这个上下文中,“可选参数”可能意味着使用类似于“-f”或“--foo”之类的选项标志来指定参数,而“预先知道确切数量的参数”可能意味着在没有任何前置选项标志的情况下给出位置参数。 - mtraceur

78

Python标准库还有一个argparse模块(在标准库的optparse模块上进行了“改进”)。以下是argparse介绍中的示例:

# script.py
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'integers', metavar='int', type=int, choices=range(10),
         nargs='+', help='an integer in the range 0..9')
    parser.add_argument(
        '--sum', dest='accumulate', action='store_const', const=sum,
        default=max, help='sum the integers (default: find the max)')

    args = parser.parse_args()
    print(args.accumulate(args.integers))

使用方法:

$ script.py 1 2 3 4
4

$ script.py --sum 1 2 3 4
10

3
its just a copy and paste - blitu12345
7
在我回答的时候,没有其他回答以任何方式提到argparse。该模块本身不在标准库中。您对文档中的代码示例有何反对意见?为什么认为需要自己编写示例而不使用模块作者提供的示例?并且我不喜欢仅包含链接的答案(我不是唯一一个持这种观点的人)。 - jfs
3
来这里的人已经知道文档中有什么内容,他们只是在这里寻求更进一步的澄清。我的情况也是如此,但我在这里真正发现的是从原始文档中复制和粘贴的内容。祝平安! - blitu12345
13
我非常怀疑这个假设:“来到这里的人已经知道文档中有什么内容。” - sjas
2
我发现很难弄清楚如何实际使用解析结果。 在这个答案中,最后一行代码解决了这个问题(所以+1)。但说实话,这个例子非常简洁,可能超出了询问如何处理命令行参数的人的能力范围。 - Wolf

74

如果你需要快速而且不是很灵活的东西

main.py:

import sys

first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)

接下来运行python main.py James Smith

将会产生以下输出:

Hello James Smith


一个更现实的用法是 python main.py "James Smith",这将在 sys.argv[1] 中放置 James Smith 并在尝试使用不存在的 sys.argv[2] 时产生 IndexError。引用行为将在您运行Python的平台和shell上有所不同。 - tripleee
14
我不同意我的用法不够实际。假设您的程序需要知道一个人的确切名字才能运行业务脚本,而在某些情况下,一个人可能有多个名字。例如,如果James Smith还有一个名字Joseph,那么当您仅使用python main.py "James Joseph Smith"时,如何区分Joseph是额外的名字还是姓氏?如果您担心索引越界,可以添加一个检查提供的参数数目的步骤。无论是否更加实际,我的示例展示了如何处理多个参数。 - Kent Munthe Caspersen
5
其他回答都是关于绘制月球着陆任务的。我只是简单地使用 gmail-trash-msg.py MessageID 命令。这个答案很直接,检测 MessageID 参数是否已传递到 sys.argv[1] - WinEunuuchs2Unix

53

docopt库非常流畅。它从应用程序的使用字符串构建一个参数字典。

例如,来自docopt的readme:

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

5
这已经迅速成为我最喜欢的方法。它是字符串解析,所以有点脆弱,但所有脆弱性都在一个地方,并且您可以在 http://try.docopt.org 上预览您的逻辑。可选和互斥参数以一种非常优雅的方式完成。 - gvoysey

53

一种方法是使用sys.argv。这将打印脚本名称作为第一个参数以及您传递给它的所有其他参数。

import sys

for arg in sys.argv:
    print arg

27
#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]

20

我自己使用optparse,但确实喜欢Simon Willison最近引入的optfunc库所采用的方向。它的工作原理是:

“通过检查函数定义(包括其参数及其默认值),构建命令行参数解析器。”

因此,例如这个函数定义:

def geocode(s, api_key='', geocoder='google', list_geocoders=False):

被转换为这个optparse帮助文本:

    Options:
      -h, --help            show this help message and exit
      -l, --list-geocoders
      -a API_KEY, --api-key=API_KEY
      -g GEOCODER, --geocoder=GEOCODER

9

我喜欢stdlib中的getopt,比如:

try:
    opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err: 
    usage(err)

for opt, arg in opts:
    if opt in ('-h', '--help'): 
        usage()

if len(args) != 1:
    usage("specify thing...")

最近我一直在尝试将类似这样的操作进行包装,以减少代码冗长(例如:使“-h”隐式)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接