在没有任何参数的情况下调用脚本时使用 Python argparse 显示帮助消息

Posted

技术标签:

【中文标题】在没有任何参数的情况下调用脚本时使用 Python argparse 显示帮助消息【英文标题】:Display help message with Python argparse when script is called without any arguments 【发布时间】:2011-05-01 20:15:13 【问题描述】:

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

./myprogram -h

或:

./myprogram --help

但是,如果我在没有任何参数的情况下运行脚本,它不会做任何事情。我想要它做的是在没有参数的情况下显示使用消息。这是怎么做到的?

【问题讨论】:

【参考方案1】:

这个答案来自 Steven Bethard on Google groups。我将其转发到这里是为了方便没有 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 suggests,使用parser.print_help(sys.stderr)打印到stderr。

【讨论】:

在第二个解决方案中,我使用parser.print_usage() 代替parser.print_help() -- 帮助消息包括用法,但更冗长。 我会投票支持答案的第二部分,但覆盖error() 对我来说似乎是一个糟糕的主意。它有不同的用途,它不是为打印友好的用法或帮助而设计的。 @Peterino - 覆盖发生在子类中,所以这应该不是问题。这是明确的。 在第二部分,考虑使用sys.exit(0)获得与./myprogram -h相同的退出状态 sys.exit(0) 表示程序成功结束。 sys.exit(1) 表示程序以失败结束。【参考方案2】:

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

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

好处是工作流程更清晰,您不需要存根类。缺点是第一个“用法”行打印了两次。

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

【讨论】:

我也是,我更喜欢这个而不是接受的答案。当参数出乎意料时,添加一个类对于打印帮助来说太过分了。让优秀的模块 argparse 为您处理错误情况。 如果使用 -h 标志,此代码将打印 2 次帮助,如果使用 --version 标志,则打印不必要的帮助。为了缓解这些问题,您可以检查如下错误类型:except SystemExit as err: if err.code == 2: parser.print_help() 为什么不直接抓住AttributeError-h--version 标志应该没问题。【参考方案3】:

使用 argparse 你可以使用ArgumentParser.print_usage():

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)

Printing help

ArgumentParser.print_usage(file=None)

  打印如何在命令行上调用ArgumentParser 的简要说明。如果fileNone,则假定sys.stdout

【讨论】:

这必须在调用parser.parse_args()之前【参考方案4】:

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

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

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

【讨论】:

这个答案被低估了。简单并且与子解析器配合得很好。 很好的答案!我所做的唯一更改是使用不带参数的 lambda。 @boh717 为了使其工作,您必须在没有任何参数的情况下调用 args.func(),而此处使用的模式(以及 argparse 文档中)是将 args 本身传递给函数。【参考方案5】:

如果您有必须为脚本运行指定的参数 - 使用 ArgumentParser 的 required 参数,如下所示:-

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

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

【讨论】:

这是最简单的解决方案,也适用于指定的无效选项。 同意。我认为利用参数解析器的内置功能总是比编写某种额外的处理程序更好。 我不确定这是否能回答实际问题。如果没有给出参数,OP 希望打印帮助消息。这个答案涉及添加一个必需的可选参数,这不是所要求的。充其量,这只会打印一条使用消息。【参考方案6】:

如果命令行中没有给出默认参数,最简洁的解决方案是手动传递默认参数:

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

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

【讨论】:

我认为这是这里列出的最好的!【参考方案7】:

把我的版本扔在这里:

import argparse

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

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

【讨论】:

不幸的是,如果缺少位置参数,parser.parse_args() 将退出。所以这只在使用可选参数时有效。 @MarcelWilson,确实如此-很好!我会考虑如何改变它。 当参数具有default 方法时,not vars(args) 可能不起作用。【参考方案8】:

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

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

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

第二个有点hackier。结合先前评估的事实,即空列表是 FalseTrue == 1False == 0 等价,您会得到:

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

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

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

【讨论】:

【参考方案9】:
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=64, message="\nSomething went wrong, --example condition was not set\n")


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

示例调用:

$ python3 ~/helloworld.py;回声$? 用法:helloworld.py [-h] [--example] 基于 argparser 的示例 python 文件 可选参数: -h, --help 显示此帮助信息并退出 --example 示例参数 出了点问题,--example 条件未设置 64 $ python3 ~/helloworld.py --example;回声$? 0

【讨论】:

【参考方案10】:

所以对于一个非常简单的答案。大多数情况下,使用 argparse 时,您会检查是否设置了参数,以调用执行某些操作的函数。

如果没有参数,则在最后输出并打印帮助。简单而有效。

import argparse
import sys
parser = argparse.ArgumentParser()

group = parser.add_mutually_exclusive_group()
group.add_argument("--holidays", action='store_true')
group.add_argument("--people", action='store_true')

args=parser.parse_args()
if args.holidays:
    get_holidays()
elif args.people:
    get_people()
else:
    parser.print_help(sys.stderr)

【讨论】:

【参考方案11】:

这里的大多数答案都需要导入另一个模块,例如 sys,或者使用可选参数。我想找到一个只使用argparse 的答案,使用所需的参数,并且如果可能的话,在不捕获异常的情况下工作。我最终得到以下结果:

import argparse

if __name__ == '__main__':

    arg_parser = argparse.ArgumentParser(add_help=False)
    arg_parser.add_argument('input_file', type=str, help='The path to the input file.')
    arg_parser.add_argument('output_file', type=str, help='The path to the output file.')
    arg_parser.add_argument('-h','--help', action='store_true', help='show this help message and exit')
    arg_parser.usage = arg_parser.format_help()
    args = arg_parser.parse_args()

主要思想是使用format_help 函数为使用语句提供帮助字符串。在对ArgumentParser() 的调用中将add_help 设置为False 可防止帮助语句在某些情况下打印两次。但是,我必须为可选帮助参数创建一个参数,该参数一旦设置为False,就模仿了典型的帮助消息,以便在帮助消息中显示可选帮助参数。该操作在帮助参数中设置为store_true,以防止帮助消息在打印帮助消息时为参数填写HELP 之类的值。

【讨论】:

【参考方案12】:

这是另一种方法,如果您需要一些灵活的东西,如果通过了特定参数,则希望在其中显示帮助,根本没有或超过 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 ...

干杯!

【讨论】:

我认为你会更容易使用子解析器或互斥组【参考方案13】:

我喜欢让事情尽可能简单,这很好用:

#!/usr/bin/env python3
Description = """Tool description"""
Epilog  = """toolname.py -a aflag -b bflag  with these combined it does blah"""
arg_parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    description=Description, 
    epilog=Epilog,
)
    try:
        if len(sys.argv) == 1:
            arg_parser.print_help()
    except Exception as e:
        print(e)

这就是我开始所有工具的方式,因为包含一些示例总是很好

【讨论】:

最佳答案:)【参考方案14】:

调用add_subparsers方法时,将第一个位置参数保存到dest=,并在argparse初始化后检查值,如下所示:

subparsers = parser.add_subparsers(dest='command')

只需检查这个变量:

if not args.command:
    parser.print_help()
    parser.exit(1)  # If exit() - exit code will be zero (no error)

完整示例:

#!/usr/bin/env python

""" doc """

import argparse
import sys

parser = argparse.ArgumentParser(description=__doc__)
subparsers = parser.add_subparsers(dest='command',
                                   help='List of commands')

list_parser = subparsers.add_parser('list',
                                    help='List contents')
list_parser.add_argument('dir', action='store',
                         help='Directory to list')

create_parser = subparsers.add_parser('create',
                                      help='Create a directory')
create_parser.add_argument('dirname', action='store',
                           help='New directory to create')
create_parser.add_argument('--read-only', default=False, action='store_true',
                           help='Set permissions to prevent writing to the directory')

args = parser.parse_args()

if not args.command:
    parser.print_help()
    parser.exit(1)

print(vars(args))  # For debug

【讨论】:

【参考方案15】:

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

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

参考Python nargs

【讨论】:

【参考方案16】:

如果您的命令是用户需要选择某些操作的内容,则使用一个互斥组with 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

【讨论】:

【参考方案17】:

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

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(...)
    ...

Here 是ArgumentParser 类的error 函数的定义。

如您所见,以下签名采用两个参数。但是,类外的函数对第一个参数self 一无所知,因为粗略地说,这个参数是针对类的。

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

您可以在_error 函数中传递parser (self),通过调用它:

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 会打印出它的原因,然后你会拦截它,查看它,然后……扔掉。

【讨论】:

【参考方案18】:

这就是我想出的:

import sys
import argparse


HelpFlags = ('help', '--help', '-h', '/h', '?', '/?', )


class ArgParser (argparse.ArgumentParser):
    
    def __init__(self, *args, **kws):
        super().__init__(*args, **kws)
    
    def parse_args(self, args=None, namespace=None):
        
        if args is None:
            args = sys.argv[1:]
        
        if len(args) < 1 or (args[0].lower() in HelpFlags):
            self.print_help(sys.stderr)
            sys.exit()
        
        return super().parse_args(args, namespace)

【讨论】:

以上是关于在没有任何参数的情况下调用脚本时使用 Python argparse 显示帮助消息的主要内容,如果未能解决你的问题,请参考以下文章

argparse 模块如何在没有任何参数的情况下添加选项?

如何使 Python 脚本独立可执行以在没有任何依赖项的情况下运行? [复制]

使用参数在python脚本之间传递函数

启用以在不使用 CLI 的情况下运行的 Python 脚本不采用命令行参数

在 python 中为 mongodb 编写 shell 脚本时出错在用户 ID 调用的情况下

从 VBScript 调用任何带有俄语名称的文件/脚本失败