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. Hibernate初识

    1. 持久化框架 狭义的概念:数据存储在物理存储介质不会丢失. 广义的概念:对数据的crud操作都叫持久化. 加载:hibernate的概念,数据从数据库中加载到session. 2. ORM(obj ...

  2. [web安全原理]PHP反序列化漏洞

    前言 这几天一直在关注新管状病毒,从微博到各大公众号朋友圈了解感觉挺严重的看微博感觉特别严重看官方说法感觉还行那就取中间的吧 自己要会对这个东西要有理性的判断.关注了好两天所以耽搁了学习emmm 希望 ...

  3. exec() has been disabled for security reasons

    1.修改php.ini里面:disable_functions 2.重启服务器 2.如果是虚拟机,就重启虚拟机

  4. 面试BAT必问的JVM,今天我们来说一说它类加载器的底层原理

    类加载器的关系 类加载器的分类 JVM支持两种类加载器,一种为引导类加载器(Bootstrap ClassLoader),另外一种是自定义类加载器(User Defined ClassLoader) ...

  5. Codeforces741D

    dsu on tree 题目链接 点我跳转 题目大意 一棵根为 \(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种) 一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经 ...

  6. MindManager 2021 版新增了哪些功能

    MindManager Windows 21是一款强大的可视化工具和思维导图软件,在工作应用中有出色的表现.今天就带大家来看下这个新版本增加了哪些功能? 1.新增现代主题信息样式MindManager ...

  7. WPF有关控件和模板样式设计的微软官方文档

    说明 如果你正在使用WPF开发应用程序,相信这篇博客会对你有用.希望你能认真的阅读 正文 此文主要以Button为例进行介绍此文档的组成部分. Button Parts Button控件没有任何命名的 ...

  8. Network-Emulator-Toolkit 模拟各种网络环境 windows

    背景.目标.目的 (1) 背景: 我们在使用网络时,时常遇到在正常网络环境下的代码运行一切正常,可以复杂的网络环境下的各种问题无法复现,必须搭建模拟各种网络环境,去复现问题,定位问题.不管是移动平台, ...

  9. vue微博回调接口

    1.vue微博回调空页面 注:微博回调空页面为: http://127.0.0.1:8888/oauth/callback/ 1.1 页面路径 components\oauth.vue <tem ...

  10. VUE中,@click后边( ) 有无括号的区别

    在使用的时候,两种方式结果效果差不多是一样. @click="Login()" @click="Login"而唯一的区别就是,有括号的可以在括号里写传递的参数. ...