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 命令行工具有很大的区别:

  1. 每次跑命令都要再敲一个python

  2. 每次还要指向指定的py文件或到指定的目录下

    首先说第一个问题,每次多敲六个字母和一个空格,作为一个linux命令行工具是非常不合格的,本来命令行工具就在追求简单化,这种指定解释器的操作我们当然要尽可能省掉咯

    第二个问题,老是指定文件的目录就更麻烦了,日常使用的时候在不同的服务器跑命令还要想想放在哪里,而且如果使用绝对路径的话,更会导致命令的冗长。

下面我们来解决一下这两个“小”问题:

  1. 在文件的第一行指定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)
  1. 增加文件的可执行权限
root@node:~# chmod +x test.py
  1. 美化以下,去掉小尾巴(仅仅是给文件改了个名字, 这一步非必须)
root@node:~# mv test.py mytool
  1. 做个软连接,可以随时随地找得到该命令
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)

函数名呢,addsubmuldiv分别对应 加、减、乘、除四则运算,每个方法都接受 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.

fire-GitHub地址

python制作命令行工具——fire的更多相关文章

  1. Python -- Scrapy 命令行工具(command line tools)

    结合scrapy 官方文档,进行学习,并整理了部分自己学习实践的内容 Scrapy是通过 scrapy 命令行工具进行控制的. 这里我们称之为 “Scrapy tool” 以用来和子命令进行区分. 对 ...

  2. Nodejs 如何制作命令行工具

    # 全局安装,安装报错是需要前面加上sudo $ sudo npm install -g xxxb # 输出帮助 $ xxxb -h Usage: xxxb 这里是我私人玩耍的命令哦![options ...

  3. [python] 自动生成命令行工具 - fire 简介

    转自 Alan Lee Python 中用于生成命令行接口(Command Line Interfaces, CLIs)的工具已经有一些了,例如已经成为 Python 标准库的 argparse 和第 ...

  4. Python借助argv和input()制作命令行工具

    命令行执行.py文件并传递参数 代码示例如下,将参数解包 from sys import argv import requests import json import time script, us ...

  5. node.js如何制作命令行工具(一)

    之前使用过一些全局安装的NPM包,安装完之后,可以通过其提供的命令,完成一些任务.比如Fis3,可以通过fis3 server start 开启fis的静态文件服务,通过fis3 release开启文 ...

  6. python 命令行工具 fire

    简介 A library for automatically generating command line interfaces. Python Fire is a library for auto ...

  7. node命令行工具—cf-cli

    音乐分享: 钢心 - <龙王> 初喜<冠军>后喜<龙王> (PS:听一次钢心乐队的演出后采访才知道 “龙王”隐喻的是一起喝酒的老铁....) ——————————— ...

  8. 基于Python与命令行人脸识别项目(系列一)

    Face Recognition 人脸识别 摘要:本项目face_recognition是一个强大.简单.易上手的人脸识别开源项目,并且配备了完整的开发文档和应用案例,方便大家使用.对于本项目可以使用 ...

  9. GitBook是一个命令行工具(Node.js库),我们可以借用该工具使用Github/Git和Markdown来制作精美的图书,但它并不是一本关于Git的教程哟。

    GitBook是一个命令行工具(Node.js库),我们可以借用该工具使用Github/Git和Markdown来制作精美的图书,但它并不是一本关于Git的教程哟. 支持输出多种格式 GitBook支 ...

随机推荐

  1. metasploit数据库使用学习

    metasploit为了方便,自动将当前工作区的内容放入数据库 首先就是工作区 -a 增加工作区,-d删除工作区 不同工作区的内容会分开储存到数据库 default工作区 test工作区 db_imp ...

  2. 算法学习笔记:Tarjan算法

    在上一篇文章当中我们分享了强连通分量分解的一个经典算法Kosaraju算法,它的核心原理是通过将图翻转,以及两次递归来实现.今天介绍的算法名叫Tarjan,同样是一个很奇怪的名字,奇怪就对了,这也是以 ...

  3. Redis多线程原理详解

    本篇文章为你解答一下问题: 0:redis单线程的实现流程是怎样的? 1:redis哪些地方用到了多线程,哪些地方是单线程? 2:redis多线程是怎么实现的? 3:redis多线程是怎么做到无锁的? ...

  4. CentOS7.X 下安装MySQL8.0(附文件)

    这是64位的安装包.如果需要32位的可以去官网下载哦.步骤一样 1 获取安装资源包 mysql-8.0.18-1.el7.x86_64.rpm-bundle.tar 链接: https://pan.b ...

  5. MySQL全面瓦解13:系统函数相关

    概述 提到MySQL的系统函数,我们前面有使用过聚合函数,其实只是其中一小部分.MySQL提供很多功能强大.方便易用的函数,使用这些函数,可以极大地提高用户对于数据库的管理效率,并更加灵活地满足不同用 ...

  6. C语言编程学习者问答第一期,看看这些问题你出现过吗?

    今天给大家分享我们学习基地的小伙伴遇到的问题,以及正确回答,看看这些问题你遇到过吗~ 1.这张图片文字"第二段"后面的说法是否有问题?   回答: 这是二进制的加法,很多人会误解减 ...

  7. [笔记] dumpsys meminfo数据与smaps文件对应关系

    通过cat /proc/$PID/smaps可以查看进程内存的详细映射情况.详细解析可以参考kernel的文档/Documentation/filesystems/proc.txt 如果我们的Andr ...

  8. mq TransientStorePool

    总得来说 有些像页高速缓存那样,为了避免页面被换出到交换区,mq申请了一块内存,并且用指定这些页面不能被操作系统换出,然后将这些内存分配给业务使用:

  9. Git基本操作(一)

    Git 使用(一) - git init 初始化仓库 - git status 仓库状态 - git add filename 单个文件加入暂存 - git add. 全部加入暂存 - git com ...

  10. 基于HAL库的STM32的DSP库详解(附FFT应用)

    1 . 建立工程,生成代码时选择包含所有库.   2. 打开 option for target 选择 Target 标签,在code generatio中,将floating point hardw ...