让你如“老”绅士般编写 Python 命令行工具的开源项目:docopt

作者:HelloGitHub-Prodesire
HelloGitHub 的《讲解开源项目》系列,项目地址:https://github.com/HelloGitHub-Team/Article
一、前言
在第一篇“初探 docopt”的文章中,我们初步掌握了使用 docopt 的三个步骤,了解了它不同于 argparse 的设计思路。
那么 docopt 的使用模式都有哪些呢?其接口描述中都支持哪些语法规则呢?本文将带你深入了解 docopt。
本系列文章默认使用 Python 3 作为解释器进行讲解。
若你仍在使用 Python 2,请注意两者之间语法和库的使用差异哦~
二、使用模式
在上一篇文章中我们提到 docopt 是通过定义一个包含特定内容的字符串,也就是接口描述,来达到描述命令行功能的目的。
那么接口描述的总体规则是这样的:
- 位于关键字
usage:(大小写不敏感)和一个可见的空行之间的文本内容会被解释为一个个使用模式。 useage:后的第一个词会被解释为程序的名称,比如下面就是一个没有命令行参数的示例程序:
Usage: cli
- 接口描述中可以包含很多有各种元素的模式,以描述命令行用法,比如:
Usage:
cli command --option <argument>
cli [<optional-argument>]
cli --another-option=<with-argument>
cli (--either-that-option | <or-this-argument>)
cli <repeating-argument> <repeating-argument>...
2.1 位置参数:
使用 < 和 > 包裹的参数会被解释为位置参数。
比如,我们可以指定两个位置参数 x 和 y ,先添加的 x 位于第一个位置,后加入的 y 位于第二个位置。那么在命令行中输入 1 2的时候,分别对应到的就是 x 和 y:
"""
Usage: cli <x> <y>
"""
from docopt import docopt
arguments = docopt(__doc__, argv=['1', '2'])
print(arguments)
其输出为:
{'<x>': '1',
'<y>': '2'}
2.2 选项参数:-o --option
以单个破折号(-)开头的的参数为短选项,以双破折号(--)开头的参数为长选项。
- 短选项支持集中表达多个短选项,比如
-abc等价于-a、-b和-c - 长选项后可跟参数,通过
空格或=指定,比如--input ARG等价于--input=ARG - 短选项后可跟参数,通可选的
空格指定,比如-f FILE等价于-fFILE
在下面这个例子中,我们希望通过 -n h 或 --name 来指定名字:
"""
Usage:
cli [options]
Options:
-n, --name NAME Set name.
"""
from docopt import docopt
arguments = docopt(__doc__, argv=['-n', 'Eric'])
print(arguments)
arguments = docopt(__doc__, argv=['-nEric'])
print(arguments)
arguments = docopt(__doc__, argv=['--name', 'Eric'])
print(arguments)
arguments = docopt(__doc__, argv=['--name=Eric'])
print(arguments)
上面的示例中,我们通过 4 种方式(2 个短选项参数方式和 2 个长选项参数方式)来指定命令行输入,其输出均为:
{'--name': 'Eric'}
需要注意的是:
--input ARG(而不是 --input=ARG)的含义是模糊不清的,因为这不能看出 ARG 究竟是选项参数,
还是位置参数。在 docopt 的使用模式中,只有在接口描述中定义了对应选项才会被解释为一个带参数的选项,
否则就会被解释为一个选项和一个独立的位置参数。
-f FILE 和 -fFILE 这种写法也有同样的模糊点。后者无法说明这究竟是一系列短选项的集合,
还是一个带参数的选项。只有在接口描述中定义了对应选项才会被解释为一个带参数的选项。
2.3 命令
这里的命令也就是 argparse 中嵌套解析器所要完成的事情,准确的说,对整个命令行程序来说,实现的是子命令。
在 docopt 中,凡是不符合 --options 或 <arguments> 约定的词,均会被解释为子命令。
在下面这个例子中,我们支持 create 和 delete 两个子命令,用来创建或删除指定路径。而 delete 命令支持 --recursive 参数来表明是否递归删除指定路径:
"""
Usage:
cli create
cli delete [--recursive]
Options:
-r, --recursive Recursively remove the directory.
"""
from docopt import docopt
arguments = docopt(__doc__)
print(arguments)
直接指定 delete -r,输出如下:
$ python3 cli.py delete -r
{'--recursive': True,
'create': False,
'delete': True}
2.4 可选元素:[optional elements]
以中括号“[]”包裹的元素(选项、参数和命令)均会被标记为可选。多个元素放在一对中括号中或各自放在中括号中是等价的。比如:
Usage: cli [command --option <argument>]
等价于:
Usage: cli [command] [--option] [<argument>]
2.5 必填元素:(required elements)
没被中括号“[]”包裹的所有元素默认都是必填的。但有时候使用小括号“()”将元素包裹住,用以标记必填是有必要的。
比如,要将多个互斥元素进行分组:
Usage: my_program (--either-this <and-that> | <or-this>)
另一个例子是,当出现一个参数时,也要求提供另一个参数,那么就可以这么写:
Usage: my_program [(<one-argument> <another-argument>)]
这个例子中 <one-argument> 和 <another-argument> 要么都出现,要么都不出现。
2.6 互斥参数:element|another
在 argparse 中要想实现互斥参数,还需要先调用 parser.add_mutually_exclusive_group() 添加互斥组,
再在组里添加参数。而在 docopt 中就特别简单,直接使用 | 进行分隔:
Usage: my_program go (--up | --down | --left | --right)
在上面的示例中,使用小括号“()”来对四个互斥选项分组,要求必填其中一个选项。
在下面的示例中,使用中括号“()”来对四个互斥选项分组,可以不填,或填其中一个选项:
Usage: my_program go [--up | --down | --left | --right]
我们还可以发散一下思路,子命令天然需要互斥,那么除了这种写法:
Usage: my_program run [--fast]
my_program jump [--high]
使用如下 | 的写法,也是等价的:
Usage: my_program (run [--fast] | jump [--high])
2.7 可变参数列表:element...
可变参数列表也就是定义参数可以有多个值。在 argparse 中,我们通过 parser.add_argument('--foo', nargs='?') 来指定,其中 nargs 可以是数字、?、+、*来表示参数个数。
在 docopt 中,自然也有相同的能力,使用省略号 ... 来实现:
Usage: my_program open <file>...
my_program move (<from> <to>)...
若要参数提供 N 个,则写 N 个参数即可,比如下面的示例中要求提供 2 个:
Usage: my_program <file> <file>
若要参数提供 0 个或多个,则配合中括号“[]”进行定义,如下 3 中定义方式等价:
Usage: my_program [<file>...]
my_program [<file>]...
my_program [<file> [<file> ...]]
若要参数提供 1 个或多个,则可以这么写:
Usage: my_program <file>...
在下面完整示例中,所获得的 arguments 是 {'<file>': ['f1', 'f2']}:
"""
Usage:
cli <file>...
"""
from docopt import docopt
arguments = docopt(__doc__, argv=['f1', 'f2'])
print(arguments)
2.8 选项简写: [options]
“[options]”用于简写选项,比如下面的示例中定义了 3 个选项:
Usage: my_program [--all --long --human-readable] <path>
--all List everything.
--long Long output.
--human-readable Display in human-readable format.
可以简写为:
Usage: my_program [options] <path>
--all List everything.
--long Long output.
--human-readable Display in human-readable format.
如果一个模式中有多个选项,那么这会很有用。
另外,如果选项包含长短选项,那么也可以用它们中的任意一个写在模式中,比如下面的示例的模式中均使用短选项:
Usage: my_program [-alh] <path>
-a, --all List everything.
-l, --long Long output.
-h, --human-readable Display in human-readable format.
2.9 [--]
当双破折号“--”不是选项时,通常用于分隔选项和位置参数,以便处理例如将文件名误认为选项的情况。
为了支持此约定,需要在位置参数前添加 [--]。
Usage: my_program [options] [--] <file>...
2.10 [-]
当单破折号“-”不是选项时,通常用于表示程序应处理 stdin,而非文件。为了支持此约定,需要在使用模式中加入 [-]。
2.11 选项描述
选项描述就是描述一系列选项参数的模式。如果使用模式中的选项定义是清晰的,那么选项描述就是可选的。
选项描述可以定义如下内容:
- 短选项和长选项代表相同含义
- 带参数的选项
- 有默认值的选项参数
选项描述的每一行需要以 - 或 -- 开头(不算空格),比如:
Options:
--verbose # 好
-o FILE # 好
Other: --bad # 坏, 没有以 "-" 开头
选项描述中,使用空格或“=”来连接选项和参数,以定义带选项的参数。参数可以使用 <Arg> 的形式,
或是使用 ARG 大写字母的形式。可用逗号“,”来分隔长短选项。比如:
-o FILE --output=FILE # 没有逗号 长选项使用 "=" 分隔
-i <file>, --input <file> # 有逗号, 长选项使用空格分隔
选项描述中每个选项定义和说明之间要有两个空格,比如:
--verbose MORE text. # 坏, 会被认为是带参数 MORE 的选项
# --version 和 MORE text. 之间应该有2个空格
-q Quit. # 好
-o FILE Output file. # 好
--stdout Use stdout. # 好,2个空格
选项描述中在说明中使用 [default: <default-value>] 来给带参数的选项赋以默认值,比如:
--coefficient=K The K coefficient [default: 2.95]
--output=FILE Output file [default: test.txt]
--directory=DIR Some directory [default: ./]
三、小结
关于 docopt 的方方面面我们都了解的差不多了,回过头来看。对于命令行元信息的定义,它比 argparse 要来的更加简洁。
argparse 像是命令式编程,调用一个个的函数逐步将命令行元信息定义清楚;而 docopt 则像是声明式编程,通过声明定义命令行元信息。
两者站在的维度不同,编程的套路也不尽相同,甚是有趣。
了解了这么多,也该练练手了。在下篇文章中,我们仍然会以 git 命令作为实战项目,看看如何使用 docopt 来实现 git 命令。
『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎留言联系我们、加入我们,让更多人爱上开源、贡献开源~
让你如“老”绅士般编写 Python 命令行工具的开源项目:docopt的更多相关文章
- 让你如绅士般基于描述编写 Python 命令行工具的开源项目:docopt
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- Python 命令行工具 argparse 模块使用详解
先来介绍一把最基本的用法 import argparse parser = argparse.ArgumentParser() parser.parse_args() 在执行 parse_args() ...
- Python 命令行之旅:使用 docopt 实现 git 命令
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- python命令行工具的使用——argparse
argparse是一个常用的库函数,使用它的时候我们在命令行中不仅仅可以运行python文件,更可以零时调整参数,十分方便. 首先,如果你只是希望传一丢丢数据进去,那么只看下面两行就行了 import ...
- python 命令行工具 fire
简介 A library for automatically generating command line interfaces. Python Fire is a library for auto ...
- 10. 通过 Dockerfile 编写 linux 命令行工具
测试 linux 压力的工具 一. 实际操作 1. 创建一个 ubuntu 的容器 docker run -it ubuntu 2. 安装 stress 工具 apt-get update & ...
- 大家都说好用的 Python 命令行库:click
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- Python进阶:都说好用的 Python 命令行库click
click 是一个以尽可能少的代码.以组合的方式创建优美的命令行程序的 Python 包.它有很高的可配置性,同时也能开箱即用. 它旨在让编写命令行工具的过程既快速又有趣,还能防止由于无法实现预期的 ...
- Python -- Scrapy 命令行工具(command line tools)
结合scrapy 官方文档,进行学习,并整理了部分自己学习实践的内容 Scrapy是通过 scrapy 命令行工具进行控制的. 这里我们称之为 “Scrapy tool” 以用来和子命令进行区分. 对 ...
随机推荐
- PDF文件预览和下载
背景:项目中实现pdf文件的预览以及下载 环境:jdk1.8.SpringBoot2.0.Maven PDF.js下载地址将下载的源码拷入项目中 修改viewer.js: 将default ...
- a149: 乘乘樂
题目: 你拿到一个整数,却忍不住想把每个位数都乘在一起.例如看到356就会想要知道3 * 5 * 6的值为何.快写个程序帮帮为了乘数字而快发疯的自己吧! 思路:把这个数每一位%10,并且再将它每次/1 ...
- 【学习笔记】第三章 python3核心技术与实践--Jupyter Notebook
可能你已经知道,Python 在 14 年后的“崛起”,得益于机器学习和数学统计应用的兴起.那为什么 Python 如此适合数学统计和机器学习呢?作为“老司机”的我可以肯定地告诉你,Jupyter N ...
- Linux ln 软、硬链接
最近在学习Linux系统的,给我的感觉就是“智慧的结晶,智慧的大脑,智慧的操作” 今天研究到了一个有趣的命令 ln 我们先来看一下它的概念吧 Linux ln命令是一个非常重要命令,它的功能是为 ...
- EasySwoole+ElasticSearch打造 高性能 小视频服务系统
EasySwoole+ElasticSearch打造高性能小视频服务 第1章 课程概述 第2章 EasySwoole框架快速上手 第3章 性能测试 第4章 玩转高性能消息队列服务 第5章 小视频服务平 ...
- [VB.NET Tips]字符串连接
在很多应用场景下我们都需要对字符串进行拼接操作. 在每一次连接字符串时,都要在堆上分配新的内存空间,每一个分配都有一定的消耗. 较长的字符串在堆中分配,对其进行连接操作需要花费很长的时间,先连接小的字 ...
- Spring Boot 的单元测试和集成测试
学习如何使用本教程中提供的工具,并在 Spring Boot 环境中编写单元测试和集成测试. 1. 概览 本文中,我们将了解如何编写单元测试并将其集成在 Spring Boot 环境中.你可在网上找到 ...
- 转载:elastic5.x部署常见问题总结
原博文名称:ElasticSearch 5.0.0 安装部署常见错误或问题 原博文地址为:http://www.dajiangtai.com/community/18136.do?origin=csd ...
- 第九周java动手动脑
1.使用Files. walkFileTree()找出指定文件夹下所有扩展名为.txt和.java的文件. import java.io.IOException; import java.nio.fi ...
- 转载:alpha测试和beta测试的区别;黑盒测试和白盒测试的区别;
alpha测试版,有点相当于内部测试,一般开发人员在场 ,是由用户做测试,但开发人员在场,一般是请用户到开发现场去测试 beta测试版,完全交给用户,由用户做测试,返回测试报告,相当于发行前的一个 ...