在不带任何参数的情况下调用脚本时,使用python argparse显示帮助消息


226

这可能很简单。假设我有一个使用argparse处理命令行参数/选项的程序。以下将打印“帮助”消息:

./myprogram -h

要么:

./myprogram --help

但是,如果我运行脚本时不带任何参数,则它不会执行任何操作。我要它做的是在不带参数的情况下显示用法消息。怎么做?

Answers:


273

这个答案来自Google网上论坛的Steven Bethard 。我将其重新发布在这里,以方便没有Google帐户的人访问。

您可以覆盖方法的默认行为error

import argparse
import sys

class MyParser(argparse.ArgumentParser):
    def error(self, message):
        sys.stderr.write('error: %s\n' % message)
        self.print_help()
        sys.exit(2)

parser = MyParser()
parser.add_argument('foo', nargs='+')
args = parser.parse_args()

请注意,上述解决方案将在error 触发方法时打印帮助消息。例如,test.py --blah如果--blah不是有效的选项,还将打印帮助消息。

如果只在命令行中未提供任何参数的情况下才打印帮助消息,那么也许这仍然是最简单的方法:

import argparse
import sys

parser=argparse.ArgumentParser()
parser.add_argument('foo', nargs='+')
if len(sys.argv)==1:
    parser.print_help(sys.stderr)
    sys.exit(1)
args=parser.parse_args()

请注意,parser.print_help()默认情况下会打印到标准输出。正如init_js建议的那样,用于parser.print_help(sys.stderr)打印到stderr。


是的..那就是我想知道的,argparse是否有办法处理这种情况。谢谢!
musashiXXX

6
在第二个解决方案中,我使用parser.print_usage()代替parser.print_help()-帮助消息包括用法,但更为冗长。
user2314737

5
我本来应该对答案的第二部分投赞成票,但error()对我而言,压倒一切似乎是一个可怕的主意。它有不同的用途,并非为打印友好的用法或帮助而设计。
Peterino'1

@Peterino-覆盖发生在子类中,所以这应该不是问题。很明显
马塞尔·威尔逊

1
@unutbu精彩!正是我需要的。一个问题,这也可以应用于子命令吗?我通常只会得到``Namespace(output = None)`。如何在所有子命令上轻松触发错误?我想在那里触发一个错误。
乔纳森·科马尔

56

除了编写类,可以使用try / except代替

try:
    options = parser.parse_args()
except:
    parser.print_help()
    sys.exit(0)

好处是工作流程更加清晰,您不需要存根类。不利的一面是第一行“用法”行被打印两次。

这将至少需要一个强制性参数。没有强制性参数,在命令行上提供零args是有效的。


我也是,我更喜欢这个,而不是公认的答案。当参数意外出现时,添加类对于打印帮助是多余的。让出色的模块argparse为您处理错误情况。
妮可·芬妮

7
如果使用-h标志,此代码将打印两次帮助,如果使用--version标志,则不必要的打印帮助。为了缓解这些问题,您可以检查如下错误类型:except SystemExit as err: if err.code == 2: parser.print_help()
pkowalczyk

25

使用argparse,您可以执行以下操作:

parser.argparse.ArgumentParser()
#parser.add_args here

#sys.argv includes a list of elements starting with the program
if len(sys.argv) < 2:
    parser.print_usage()
    sys.exit(1)

5
必须致电parser.parse_args()
Bob Stein

18

如果必须为运行脚本指定参数,请对ArgumentParser 使用必需的参数,如下所示:-

parser.add_argument('--foo', required=True)

如果脚本不带任何参数运行,parse_args()将报告错误。


2
这是最简单的解决方案,也可以使用指定的无效选项。
史蒂夫·谢勒

1
同意 我认为最好利用参数解析器的内置功能,然后再编写某种其他处理程序。
Christopher Hunter,

18

如果您为(子)解析器关联了默认功能,如所述add_subparsers,您可以简单地将其添加为默认操作:

parser = argparse.ArgumentParser()
parser.set_defaults(func=lambda x: parser.print_usage())
args = parser.parse_args()
args.func(args)

如果由于缺少位置参数而引发异常,请添加try-except。


1
这个答案太低估了。很简单,与子解析器配合得很好。
orodbhen

好答案!我所做的唯一更改是使用了不带参数的lambda。
boh717

12

最干净的解决方案是,如果在命令行中未提供默认参数,则手动传递默认参数:

parser.parse_args(args=None if sys.argv[1:] else ['--help'])

完整的例子:

import argparse, sys

parser = argparse.ArgumentParser()
parser.add_argument('--host', default='localhost', help='Host to connect to')
# parse arguments
args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

# use your args
print("connecting to {}".format(args.host))

如果不带参数调用,这将打印出完整的帮助(不是简短用法)。


2
sys.argv[1:]是一个非常常见的成语。我看到了parser.parse_args(None if sys.argv[1:] else ['-h'])更多惯用和清洁的东西。
NunoAndré18年

1
@NunoAndré谢谢-更新了答案。确实感觉更pythonic。
Ievgen Popovych '18

10

将我的版本扔到这里:

import argparse

parser = argparse.ArgumentParser()
args = parser.parse_args()
if not vars(args):
    parser.print_help()
    parser.exit(1)

您可能会注意到parser.exit-我主要是这样做的,因为如果这是sys文件中唯一的原因,它会保存导入行...


parser.exit(1)很好!很好的补充。
cgseller 2015年

4
不幸的是,如果缺少位置参数,parser.parse_args()将退出。因此,这仅在使用可选参数时有效。
马塞尔·威尔逊

1
@MarcelWilson,的确确实如此-不错!我将考虑如何更改它。
pauricthelodger 2016年

not vars(args)当参数具有default方法时,可能不起作用。
funkid

5

有一对可以完成这项工作的单行代码sys.argv[1:](这是一个非常常见的Python习惯用法,用于引用命令行参数,sys.argv[0]即脚本的名称)。

第一个是不言自明的,干净的和pythonic的:

args = parser.parse_args(None if sys.argv[1:] else ['-h'])

第二个是一个小黑客。结合先前评估的事实,即空列表FalseTrue == 1False == 0等价物,您将获得以下信息:

args = parser.parse_args([None, ['-h']][not sys.argv[1:]])

也许括号太多,但很清楚是否选择了先前的参数。

_, *av = sys.argv
args = parser.parse_args([None, ['-h']][not av])

1
parser.print_help()
parser.exit()

parser.exit方法还接受status(返回码)和message值(您自己包括尾随换行符!)。

一个有思想的例子,:)

#!/usr/bin/env python3

""" Example argparser based python file
"""

import argparse

ARGP = argparse.ArgumentParser(
    description=__doc__,
    formatter_class=argparse.RawTextHelpFormatter,
)
ARGP.add_argument('--example', action='store_true', help='Example Argument')


def main(argp=None):
    if argp is None:
        argp = ARGP.parse_args()  # pragma: no cover

    if 'soemthing_went_wrong' and not argp.example:
        ARGP.print_help()
        ARGP.exit(status=128, message="\nI just don't know what went wrong, maybe missing --example condition?\n")


if __name__ == '__main__':
    main()  # pragma: no cover

示例调用:

$ python3〜/ helloworld.py; 回声$?
用法:helloworld.py [-h] [--example]

 示例基于argparser的python文件

可选参数:
  -h,--help显示此帮助消息并退出
  --example示例参数

我只是不知道出了什么问题,也许会丢失-示例条件?
128
$ python3〜/ helloworld.py-示例; 回声$?
0

0

使用nargs设置位置参数,并检查位置args是否为空。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='?')
args = parser.parse_args()
if not args.file:
    parser.print_help()

参考Python nargs


0

这是另一种实现方法,如果您需要一些灵活的方法,如果传递了特定的参数,则希望在显示帮助的地方显示帮助,则完全没有或超过1个冲突的arg:

import argparse
import sys

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-d', '--days', required=False,  help="Check mapped inventory that is x days old", default=None)
    parser.add_argument('-e', '--event', required=False, action="store", dest="event_id",
                        help="Check mapped inventory for a specific event", default=None)
    parser.add_argument('-b', '--broker', required=False, action="store", dest="broker_id",
                        help="Check mapped inventory for a broker", default=None)
    parser.add_argument('-k', '--keyword', required=False, action="store", dest="event_keyword",
                        help="Check mapped inventory for a specific event keyword", default=None)
    parser.add_argument('-p', '--product', required=False, action="store", dest="product_id",
                        help="Check mapped inventory for a specific product", default=None)
    parser.add_argument('-m', '--metadata', required=False, action="store", dest="metadata",
                        help="Check mapped inventory for specific metadata, good for debugging past tix", default=None)
    parser.add_argument('-u', '--update', required=False, action="store_true", dest="make_updates",
                        help="Update the event for a product if there is a difference, default No", default=False)
    args = parser.parse_args()

    days = args.days
    event_id = args.event_id
    broker_id = args.broker_id
    event_keyword = args.event_keyword
    product_id = args.product_id
    metadata = args.metadata
    make_updates = args.make_updates

    no_change_counter = 0
    change_counter = 0

    req_arg = bool(days) + bool(event_id) + bool(broker_id) + bool(product_id) + bool(event_keyword) + bool(metadata)
    if not req_arg:
        print("Need to specify days, broker id, event id, event keyword or past tickets full metadata")
        parser.print_help()
        sys.exit()
    elif req_arg != 1:
        print("More than one option specified. Need to specify only one required option")
        parser.print_help()
        sys.exit()

    # Processing logic here ...

干杯!


我认为您将更容易使用subparsers或common_exclusive_group
Tim Bray

0

如果您的命令是用户需要选择某些操作的命令,则使用具有required = True的互斥组。

这是对pd321给出的答案的扩展。

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--batch", action='store', type=int,  metavar='pay_id')
group.add_argument("--list", action='store_true')
group.add_argument("--all", action='store_true', help='check all payments')

args=parser.parse_args()

if args.batch:
    print('batch {}'.format(args.batch))

if args.list:
    print('list')

if args.all:
    print('all')

输出:

$ python3 a_test.py
用法:a_test.py [-h](--batch pay_id | --list | --all)
a_test.py:错误:--batch --list --all参数之一是必需的

这仅提供基本帮助。其他一些答案将为您提供完整的帮助。但是至少您的用户知道他们可以做到-h


0

这不好(也因为拦截了所有错误),但是:

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)

    parser.add_argument(...)
    ...

这是该类error功能的定义ArgumentParser

https://github.com/python/cpython/blob/276eb67c29d05a93fbc22eea5470282e73700d20/Lib/argparse.py#L2374

。如您所见,签名后需要两个参数。但是,类外部的函数对第一个参数一无所知:self,因为,大致来说,这是该类的参数。(我知道,你知道...)因此,只是通过自己selfmessage_error(...)不能(

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error
    ...
...

将输出:

...
"AttributeError: 'str' object has no attribute 'print_help'"

)。您可以通过调用函数来传递parserself_error

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)
    ...
...

,但您现在不想退出程序。然后返回它:

def _error(parser):
    def wrapper():
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

。但是,parser不知道它是否已被修改,因此当发生错误时,它将发送原因(顺便说一下,其本地化翻译)。好吧,然后拦截它:

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

。现在,当发生错误并parser发送错误原因时,您将拦截它,看着它,然后……抛出。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.