python制作命令行工具——fire
python制作命令行工具——fire
前言
本篇教程的目的是希望大家可以通读完此篇之后,可以使用python制作一款符合自己需求的linux工具。
本教程使用的是google开源的python第三方库:fire
无论是学生党自己做着练手,还是工作中确有需求,本篇都尽可能通过简单的例子来示范该第三方库的用法,其中若有描述不当的地方,望留言指出。
快速介绍
来一波官方介绍。
- Python Fire是一个库,用于从任何Python对象自动生成命令行接口。
- 是用python创建CLI的一种简单方法。
- 是开发和调试Python代码的一个有用工具。
- Python Fire帮助探索现有代码或将其他人的代码转换为CLI。
- 使得Bash和Python之间的转换更加容易。
- 通过使用已经导入和创建的模块和变量来设置REPL, Python Fire使使用Python REPL变得更容易。
没听懂 ???
不是太明白 ???
不要紧,看完本篇就懂了。
快速安装
- pip安装:
pip install fire - conda安装:
conda install fire -c conda-forge - 源码安装:
1. git clone https://github.com/google/python-fire.git
2. cd python-fire
3. python setup.py install
Github地址:python-fire
快速上手
实践出真知
创建一个test.py文件,写入以下内容
import fire
def test(your_var="default_value"):
return 'This is a test ! value : %s' % your_var
if __name__ == '__main__':
fire.Fire(test)
咱们来看一下效果
# 缺省参数
root@node:~# python test.py
This is a test ! value : default_value
# 关键字参数
root@node:~# python test.py --your_var=newValue
This is a test ! value : newValue
# 位置参数
root@node:~# python test.py localtionValue
This is a test ! value : localtionValue
现在呢,我们反过头来看一下官方介绍的第一行:
Python Fire是一个库,用于从任何Python对象自动生成命令行接口。
注意关键字:任何python对象。这意味着什么?
我们来看一段代码:
import fire
boy_name = 'XiaoMing'
girl_name = 'XiaoHong'
if __name__ == '__main__':
fire.Fire()
试一下:python test.py boy_name
是不是明白了些什么。
聊完这缺省参数、关键字参数、位置参数,当然不能少了 *args 和 ** kwargs .
还是来看代码示例:
import fire
def run(*args):
arg_list = list(args)
return ' | '.join(arg_list)
if __name__ == '__main__':
fire.Fire(run)
跑一下就懂啦
root@node:~# python test.py run qwe rty uio asd fgh
qwe | rty | uio | asd | fgh
官方给的示例是这个样子的~~~
import fire
def order_by_length(*items):
"""Orders items by length, breaking ties alphabetically."""
sorted_items = sorted(items, key=lambda item: (len(str(item)), str(item)))
return ' '.join(sorted_items)
if __name__ == '__main__':
fire.Fire(order_by_length)
就是加了个长度和字母顺序的排序,来跑一下,看一下效果:
$ python example.py dog cat elephant
cat dog elephant
除此之外呢,我们还可以给输出结果加点料,还是刚才我们写的那个例子:
root@node:~# python test.py run qwe rty uio asd fgh - upper
QWE | RTY | UIO | ASD | FGH
在这里,我们通过命令行对传入的对象和调用结果执行相同的操作,譬如这里的 upper
敲黑板划重点:分隔符 “ - ” 之后的所有参数都将用于处理函数的结果,而不是传递给函数本身。默认的分隔符是连字符 “ - ”。
默认的分隔符也是可以改的,用到了fire的内置参数。
root@node:~# python test.py run qwe rty uio asd fgh X upper -- --separator=X
QWE | RTY | UIO | ASD | FGH
其中的separator就是fire的一个内置参数,更多内置参数文末有提到。
我们再来看一下fire给我们提供的命令行传参时,数据的类型。比较特殊的是,fire根据值决定类型。
import fire
fire.Fire(lambda obj: type(obj).__name__)
如果有刚学python的小伙伴,记得一定要学一下lambda函数,在这里我可以转化为普通写法。
import fire
def test(obj):
return type(obj).__name__
if __name__ == '__main__':
fire.Fire(test)
通过简单的一行代码来看一下各种数据类型如何通过命令行传参:
$ python example.py 10
int
$ python example.py 10.0
float
$ python example.py hello
str
$ python example.py '(1,2)'
tuple
$ python example.py [1,2]
list
$ python example.py True
bool
$ python example.py {name:David}
dict
但是当你想传递一个str类型的10,你就要注意了,看以下例子:
$ python example.py 10
int
$ python example.py "10"
int
$ python example.py '"10"'
str
$ python example.py "'10'"
str
$ python example.py \"10\"
str
我们可以看到,你虽然敲了"10",但是依然被判定为int,bash会自动处理掉你参数的第一层引号。所以,如果想传str类型的10,要再加一层引号,单双引号分开用,或者把引号转义。
如果要传的是dict参数,那就更要小心谨慎了。
# 标准写法
$ python example.py '{"name": "David Bieber"}'
dict
# 要这么写也没啥问题
$ python example.py {"name":'"David Bieber"'}
dict
# 但要这么写就解析成了字符串了
$ python example.py {"name":"David Bieber"}
str
# 再加个空格,字符串都不是了
$ python example.py {"name": "David Bieber"} # Wrong. This isn't even treated as a single argument.
<error>
到这里,我想大家应该大概明白了 fire 的方便快捷之处。
到了这一步的时候,虽然实现了基本功能,但还是和平时我们使用的 linux 命令行工具有很大的区别:
每次跑命令都要再敲一个python
每次还要指向指定的py文件或到指定的目录下
首先说第一个问题,每次多敲六个字母和一个空格,作为一个linux命令行工具是非常不合格的,本来命令行工具就在追求简单化,这种指定解释器的操作我们当然要尽可能省掉咯
第二个问题,老是指定文件的目录就更麻烦了,日常使用的时候在不同的服务器跑命令还要想想放在哪里,而且如果使用绝对路径的话,更会导致命令的冗长。
下面我们来解决一下这两个“小”问题:
- 在文件的第一行指定python解释器,这样就无需在我们运行该文件时再指定解释器
#!/usr/bin/python
import fire
def test(your_var="default_value"):
return 'This is a test ! value : %s' % your_var
if __name__ == '__main__':
fire.Fire(test)
- 增加文件的可执行权限
root@node:~# chmod +x test.py
- 美化以下,去掉小尾巴(仅仅是给文件改了个名字, 这一步非必须)
root@node:~# mv test.py mytool
- 做个软连接,可以随时随地找得到该命令
root@node:~# ln -s /root/mytool /usr/bin/mytool
附:如果需要指定编码的话,可以在文件头部加一行,比如
#!/usr/bin/python
# coding: utf-8
这个时候,我们随意在服务器的任意位置执行
root@node:~# mytool
This is a test ! value : default_value
root@node:~# mytool --your_var=newValue
This is a test ! value : newValue
root@node:~# mytool localtionValue
This is a test ! value : localtionValue
Perfection !
如果你已经走到这一步的话,其实已经能写很多简单的命令行工具了。
为什么说简单呢,目前都是使用函数来完成一个个命令的逻辑,多一个子命令多写一个函数,慢慢的就会让这个文件变的庞杂和冗余。而且久而久之,肯定会出现一些看起来很相似,却要使用ctrl + c-v大法去完成的事情。甚至有一些逻辑,想要实现还要自己去做更复杂的逻辑。
快速进阶
此时,一年级的已经可以下课了,二年级的请注意听讲了,下面,我们要讲的是:
类的使用
命令嵌套
属性访问
链式调用
类的使用
通过一个简单的算数类来了解其用法,在下列用法中,我们在fire中注册了一个类对象。
import fire
class Calculator(object):
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
if __name__ == '__main__':
calculator = Calculator()
fire.Fire(calculator)
以下是调用测试
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
当然我们也可以注册一个类。
import fire
class Calculator(object):
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
if __name__ == '__main__':
fire.Fire(Calculator)
跑一下看看:
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
就这?当然不会,我们还可以通过参数控制实例属性,就像下面的例子:
import fire
class BrokenCalculator(object):
def __init__(self, offset=1):
self._offset = offset
def add(self, x, y):
return x + y + self._offset
def multiply(self, x, y):
return x * y + self._offset
if __name__ == '__main__':
fire.Fire(BrokenCalculator)
我们可以看到,新增了一个offset的实例属性,缺省值是1.
$ python example.py add 10 20
31
$ python example.py multiply 10 20
201
重点来了,我们可以直接给属性赋值,以此来增加你命令行工具的自由度。
$ python example.py add 10 20 --offset=0
30
$ python example.py multiply 10 20 --offset=0
200
命令嵌套
通过不同的类来控制某些同名命令,其实也是将各个命令分门别类更具条理性的管理。可以看到以下用法。
import fire
class Sing:
def run(self):
print('sing sing sing ...')
class Dance:
def run(self):
print('dance dance dance ...')
def status(self):
print('Around.')
class Pipeline:
def __init__(self):
self.sing = Sing()
self.dance = Dance()
def run(self):
self.sing.run()
self.dance.run()
self.dance.status()
if __name__ == '__main__':
fire.Fire(Pipeline)
跑跑看:
# python3 ball.py run
sing sing sing ...
dance dance dance ...
Around.
# python3 ball.py sing run
sing sing sing ...
# python3 ball.py dance run
dance dance dance ...
# python3 ball.py dance status
Around.
根据自定义的一个Pipeline类,我们可以自己组合想要的命令行效果,给子命令再分配不同的子集。
属性访问
其实前面说到类的时候已经简单的说过属性访问(就是那个offset的例子,行啦,忘了就不用往上翻了),这里再详细举例说明一下。
# python3 test.py --age=6 outinfo
Xiao Ming is 6 years old and in the First grade
# python3 test.py --age=7 outinfo
Xiao Ming is 7 years old and in the Second grade
# python3 test.py --age=8 outinfo
Xiao Ming is 8 years old and in the Third grade
综上,我们可以通过控制类的属性来构造类对象。
唠到这儿了,再唠一个骚操作
链式调用
官方给的例子不太好看,没有那么让人一眼就看懂的感觉,找了个四则运算的简单示例:
import fire
class Calculator:
def __init__(self):
self.result = 0
self.express = '0'
def __str__(self):
return f'{self.express} = {self.result}'
def add(self, x):
self.result += x
self.express = f'{self.express}+{x}'
return self
def sub(self, x):
self.result -= x
self.express = f'{self.express}-{x}'
return self
def mul(self, x):
self.result *= x
self.express = f'({self.express})*{x}'
return self
def div(self, x):
self.result /= x
self.express = f'({self.express})/{x}'
return self
if __name__ == '__main__':
fire.Fire(Calculator)
函数名呢,add、sub、mul、div分别对应 加、减、乘、除四则运算,每个方法都接受 x 参数去运算,返回self,这样不论往后链式调用多少次都可以,结束调用到 __str__ 打印出结果。
__str__ 在 fire 中用来完成自定义序列化。如果不提供这个方法,在链式调用完成后将会打印帮助内容。
# python3 test.py add 2 sub 1.5 mul 3 div 2
((0+2-1.5)*3)/2 = 0.75
# python3 test.py add 4 sub 2.5 mul 2 div 4 mul 3 sub 5 add 2
(((0+4-2.5)*2)/4)*3-5+2 = -0.75
看完这个大家应该明白链式调用的运用了,这个时候再来看一下官方示例也许会轻松一些。
import fire
class BinaryCanvas(object):
"""A canvas with which to make binary art, one bit at a time."""
def __init__(self, size=10):
self.pixels = [[0] * size for _ in range(size)]
self._size = size
self._row = 0 # The row of the cursor.
self._col = 0 # The column of the cursor.
def __str__(self):
return '\n'.join(' '.join(str(pixel) for pixel in row) for row in self.pixels)
def show(self):
print(self)
return self
def move(self, row, col):
self._row = row % self._size
self._col = col % self._size
return self
def on(self):
return self.set(1)
def off(self):
return self.set(0)
def set(self, value):
self.pixels[self._row][self._col] = value
return self
if __name__ == '__main__':
fire.Fire(BinaryCanvas)
跑一下看看:
$ python example.py move 3 3 on move 3 6 on move 6 3 on move 6 6 on move 7 4 on move 7 5 on
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
PS:我要不说,谁能看出来这是个笑脸???
最后一课
最后看看官方给出的 fire 的内置参数吧,具体怎么应用大家就自己研究咯。
Flags
| Using a CLI | Command | Notes |
|---|---|---|
| Help | command -- --help |
Show help and usage information for the command. |
| REPL | command -- --interactive |
Enter interactive mode. |
| Separator | command -- --separator=X |
This sets the separator to X. The default separator is -. |
| Completion | command -- --completion [shell] |
Generate a completion script for the CLI. |
| Trace | command -- --trace |
Gets a Fire trace for the command. |
| Verbose | command -- --verbose |
Include private members in the output. |
python制作命令行工具——fire的更多相关文章
- Python -- Scrapy 命令行工具(command line tools)
结合scrapy 官方文档,进行学习,并整理了部分自己学习实践的内容 Scrapy是通过 scrapy 命令行工具进行控制的. 这里我们称之为 “Scrapy tool” 以用来和子命令进行区分. 对 ...
- Nodejs 如何制作命令行工具
# 全局安装,安装报错是需要前面加上sudo $ sudo npm install -g xxxb # 输出帮助 $ xxxb -h Usage: xxxb 这里是我私人玩耍的命令哦![options ...
- [python] 自动生成命令行工具 - fire 简介
转自 Alan Lee Python 中用于生成命令行接口(Command Line Interfaces, CLIs)的工具已经有一些了,例如已经成为 Python 标准库的 argparse 和第 ...
- Python借助argv和input()制作命令行工具
命令行执行.py文件并传递参数 代码示例如下,将参数解包 from sys import argv import requests import json import time script, us ...
- node.js如何制作命令行工具(一)
之前使用过一些全局安装的NPM包,安装完之后,可以通过其提供的命令,完成一些任务.比如Fis3,可以通过fis3 server start 开启fis的静态文件服务,通过fis3 release开启文 ...
- python 命令行工具 fire
简介 A library for automatically generating command line interfaces. Python Fire is a library for auto ...
- node命令行工具—cf-cli
音乐分享: 钢心 - <龙王> 初喜<冠军>后喜<龙王> (PS:听一次钢心乐队的演出后采访才知道 “龙王”隐喻的是一起喝酒的老铁....) ——————————— ...
- 基于Python与命令行人脸识别项目(系列一)
Face Recognition 人脸识别 摘要:本项目face_recognition是一个强大.简单.易上手的人脸识别开源项目,并且配备了完整的开发文档和应用案例,方便大家使用.对于本项目可以使用 ...
- GitBook是一个命令行工具(Node.js库),我们可以借用该工具使用Github/Git和Markdown来制作精美的图书,但它并不是一本关于Git的教程哟。
GitBook是一个命令行工具(Node.js库),我们可以借用该工具使用Github/Git和Markdown来制作精美的图书,但它并不是一本关于Git的教程哟. 支持输出多种格式 GitBook支 ...
随机推荐
- IDEA 2019.3.3 + Pycharm 2020.2.1 安装包及破解步骤
IDEA IDEA的破解流程就不用再说了,免费试用,添加VMOptions参数,选择破解jar的路径,重启IDEA. 下载地址:链接:https://pan.baidu.com/s/1aTRATVTL ...
- 精尽MyBatis源码分析 - MyBatis 的 SQL 执行过程(一)之 Executor
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- Kafka入门(安装及使用)
Kafka是一种分布式的,基于发布/订阅的消息系统. Kafka的组成包括: Kafka将消息以topic为单位进行归纳. 将向Kafka topic发布消息的程序成为producers. 将预订to ...
- 【建议收藏】阿里P7总结的Spring注解笔记,把组件注册讲的明明白白
环境搭建 注解的方式是通过配置类的方式来注入组件,注解注入要比XML注入的方式简单,注解注入也需要在前者的基础上,添加一个spring-context的包,也是实际开发中常用的方式. 准备所需Jar包 ...
- 金九银十想面BAT?那这些JDK 动态代理的面试点你一定要知道
一.什么是代理 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 代理模式UM ...
- 如何用CorelDRAW画箭头?
CorelDRAW,简称为cdr,是一款专业的矢量绘图软件,在设计界也是常用的专业设计之一,在日常的设计工作中,我们常常需要绘制一些特殊的图形,比如箭头.很多对cdr不是特别熟练的小伙伴不知道如何用c ...
- JS 数组对象
定义数组: 数组对象用来在单独的变量名中存储一系列的值. 创建一个数组有三种方法. 1: 常规方式: var myCars=new Array(); myCars[0]="Saab" ...
- otter搭建
转载: https://blog.csdn.net/inthat/article/details/93595156 https://www.cnblogs.com/Inspire-Yi/p/80943 ...
- jvm系列(二)jvm垃圾收集器与内存分配策略
众所周知,在java语言中,内存分配和回收是由jvm自动管理的.因此内存的分配和回收也是jvm三大功能之一.垃圾收集器(GC)需要完成三件事情: 哪些内存需要回收? 什么时候进行回收? 如何回收? 本 ...
- windowsAPI函数操作注册表实现软件开机自启
注册表的结构 注册表是一个数据库,它的结构同逻辑磁盘类似.注册表包含键(Key),它类似磁盘中的目录,注册表还包含键值(Value),它类似磁盘中的文件.一个键可以包含多个子健和键值,其中键值用于存储 ...