AberSheeran
Aber Sheeran

好看易用的命令行

起笔自
所属文集: Python-Package
共计 6258 个字符
落笔于

在之前的一篇文章输出彩色命令行里,我介绍了 Colorama 的基本使用方法。但作为一个命令行程序,除了好看的五颜六色的输出,还需要解析命令行参数。

一个简单好用的库——click,不仅集成了 Colorama,还提供了更简单的命令行解析。

Command(命令)

使用click创建一个命令十分简单:

import click


@click.command(help="just say hi")
def sayhi():
    print('hi')


if __name__ == "__main__":
    sayhi()

尝试执行这个文件,你将能看一个平凡的hi

那么接下来,尝试让它接受一些参数、或者选项。

Argument(参数)

import click


@click.command(help="just say hi")
@click.argument("name")
def sayhi(name):
    print(f'hi {name}')


if __name__ == "__main__":
    sayhi()

再尝试直接运行这个文件,你能看到如下报错

 AberS@SHEERAN  ~\Desktop
 python .\sayhi.py
Usage: sayhi.py [OPTIONS] NAME
Try "sayhi.py --help" for help.

Error: Missing argument "NAME".

或许你在想,是不是因为调用sayhi的时候缺少了参数name,但并不是。@click.command会将函数签名抹去,当你直接调用sayhi的时候,实际直接调用的并不是sayhi

试试执行python sayhi.py aber这个命令,你将能看到hi aber

多值参数

接下来尝试让这个小脚本同时向更多人打招呼:

import click


@click.command(help="just say hi")
@click.argument("name", nargs=-1)
def sayhi(name):
    print('hi', ", ".join(name))


if __name__ == "__main__":
    sayhi()

nargs 选项允许 argument 接受更多的内容,当它的值为正整数时,接受指定数目的内容;当它的值为-1时,它将接受所有的参数。所以一个command下,只能有一个argument的nargs被指定为-1

❯ python .\sayhi.py aber alice bob sherry
hi aber, alice, bob, sherry

Option(选项)

选项分长选项(--option)和短选项(-o),一般来说是相对应的。

import click


@click.command(help="just say hi")
@click.option("-k", "--keyword", required=True)
@click.argument("name", nargs=-1)
def sayhi(name, keyword):
    print('hi', keyword, ", ".join(name))


if __name__ == "__main__":
    sayhi()

必须选项

执行命令时不带选项,则默认为None。但如果给option加上required=True,则必须携带此选项。

 python .\sayhi.py aber
Usage: sayhi.py [OPTIONS] [NAME]...
Try "sayhi.py --help" for help.

Error: Missing option "-k" / "--keyword".

规避关键词冲突

有些时候,选项名可能会与Python的关键词冲突,如下:

import click


@click.command(help="just say hi")
@click.option("-k", "--keyword", required=True)
@click.option('--from', '-f', 'from_')
@click.argument("name", nargs=-1)
def sayhi(from_, name, keyword):
    print(from_, ': hi', keyword, ", ".join(name))


if __name__ == "__main__":
    sayhi()

试试执行命令python sayhi.py aber -k ", what are you doing now?"

❯ python .\sayhi.py aber -k ", what are you doing now?"
None : hi , what are you doing now? aber

如果稍微敏锐一点,你可能会注意到,函数参数值与位置无关,这是因为click是通过关键词参数传递参数值的。

选项默认值

选项可以被设置默认值,并且在--help命令下显示出来。

import click


@click.command(help="just say hi")
@click.option("-k", "--keyword", required=True)
@click.option('--from', '-f', 'from_', default="Aber", show_default=True)
@click.argument("name", nargs=-1)
def sayhi(from_, name, keyword):
    print(from_, ': hi', keyword, ", ".join(name))


if __name__ == "__main__":
    sayhi()

然后看看执行结果:

❯ python .\sayhi.py Sherry -k ", what are you doing now?"
Aber : hi , what are you doing now? Sherry
 python .\sayhi.py --help
Usage: sayhi.py [OPTIONS] [NAME]...

  just say hi

Options:
  -k, --keyword TEXT  [required]
  -f, --from TEXT     [default: Aber]
  --help              Show this message and exit.

多值选项

同样的,option也提供了nargs参数用以接收多个值,与参数类似。

多选项值

设置 multiple=True 即可允许接受 -m bob -m sherry 这类选项。

布尔类型选项

click推荐给布尔类型的选项一对值,在修改默认值时会比较方便。

import click


@click.command(help="just say hi")
@click.option("-k", "--keyword", required=True)
@click.option('--from', '-f', 'from_', default="Aber", show_default=True)
@click.option('--debug/--no-debug', default=False)
@click.argument("name", nargs=-1)
def sayhi(from_, name, keyword, debug):
    print(debug, from_, ': hi', keyword, ", ".join(name))


if __name__ == "__main__":
    sayhi()

执行结果如下:

❯ python .\sayhi.py Sherry -k ", what are you doing now?"
False Aber : hi , what are you doing now? Sherry
❯ python .\sayhi.py Sherry -k ", what are you doing now?" --debug
True Aber : hi , what are you doing now? Sherry
❯ python .\sayhi.py Sherry -k ", what are you doing now?" --no-debug
False Aber : hi , what are you doing now? Sherry

但有些时候,或许只是需要开启或者不开启,而不是需要在开启/关闭状态间显式的指定。

@click.option('--debug/--no-debug', default=False)修改为@click.option('--debug', is_flag=True)。它的执行效果与上面的相同,但不能接受--no-debug的选项。

组合命令

更多的时候,我们需要构建的命令行程序由多个子命令组成。以pipenv为例,pipenv本身是一个命令,同时它也拥有众多子命令。

click提供了这样的能力,使用click.group,这将允许把一些命令挂载主命令下。

把原来的click.command替换为click.group,然后加入子命令。

import click


@click.group(help="just say hi")
@click.option("-k", "--keyword", required=True)
@click.option('--from', '-f', 'from_', default="Aber", show_default=True)
@click.option('--debug', is_flag=True)
@click.argument("name", nargs=-1)
def sayhi(from_, name, keyword, debug):
    print(debug, from_, ': hi', keyword, ", ".join(name))


@sayhi.command(help="just say bye")
def saybye():
    print("bye")


if __name__ == "__main__":
    sayhi()

尝试一下 --help 能看到子命令已经成功加入了。

 python .\sayhi.py --help
Usage: sayhi.py [OPTIONS] [NAME]... COMMAND [ARGS]...

  just say hi

Options:
  -k, --keyword TEXT  [required]
  -f, --from TEXT     [default: Aber]
  --debug
  --help              Show this message and exit.

Commands:
  saybye  just say bye

其他功能

多彩输出

click的secho函数提供了一个比较好用的输出功能。

click.secho("hello world", fg="green")

fg可用的选项有:

  • black (可能是灰色)

  • red

  • green

  • yellow (可能是橘色)

  • blue

  • magenta

  • cyan

  • white (可能是亮灰色)

  • bright_black

  • bright_red

  • bright_green

  • bright_yellow

  • bright_blue

  • bright_magenta

  • bright_cyan

  • bright_white

  • reset (重置颜色)

如果不想在最后输出换行符可以设置nl=False

click.secho("hello world", fg="red", nl=False)

Prompts(输入)

click.prompts在默认情况下支持任何unicode输入,但你也可以指定输入类型

value = click.prompt('Please enter a valid integer', type=int)

在提供了默认值时,类型会被自动确定

value = click.prompt('Please enter a number', default=42.0)

更多的功能例如进度条、打开文件、执行文件等,可看click/utils

如果你觉得本文值得,不妨赏杯茶
Aligi
poetry 使用指北