前言 上周学习了Python的基本语法,本着学以致用的想法。就试着用Python写一个自己的命令行工具,把所有常用的命令,工具都放进来,需要用的时候就不用再去google,或者history了。
为什么学习Python 既然学习一门语言,就应该有个目标,我学python,不是把它当成主要的开发语言。而是把它当成辅助性脚本语言。需要的时候,可以写一些工具。没事还可以尝试一下网上现在很流行各种python爬虫和AI库。
命令行工具应该怎么写?是什么样的? 想写一个命令行工具,首先想到的是系统自带的shell,可是这个太low了,不考虑。而且我们的主要目的还是为了用python练手。
最简单的CLI应该是,在命令行中运行一个程序,接收一个参数,在程序中接收这个参数,进行一些运算,并返回。但是这样只能称之为一个可以命令行运行并接收参数的程序。
而我们常用的系统命令,ls,cd,mkdir都是这样的。而后加装的,比如android的adb。mac os上的brew
1 2 3 ➜ MyUtils brew --version Homebrew 1.6.9 Homebrew/homebrew-core (git revision 2f937f7; last commit 2018-07-02)
我们在使用第三方程序的时候,都知道它们肯定有help,或者version等等命令。其实这些都是在遵守一个规范,同时也方便大家使用。如果想详细了解这个规范请自行google POSIX规范
Plumbum plumbum库是一个python库,帮助我们实现命令行程序,在这里我也用到了它。
官网 写的很详细,用法也很多,我们这里只重点介绍CLI的部分。
使用
命令可以安装这个库,如果你使用的是Anaconda,可以使用下面的命令下载
1 conda install -c conda-forge plumbum
开始咯 基本信息设置 首先我们先新建一个类,继承刚刚提到的Plumbum模块包中的cli.Application
1 2 3 4 5 6 7 8 9 10 from plumbum import cli, colorsclass DemoApp (cli.Application) : def main (self, *args) : pass if __name__ == '__main__' : DemoApp.run()
我们去命令行里试先试一下,应该是什么都不会有。
其实这时候如果输入
1 python3 cmddemo.py -h 或者 python3 cmddemo.py --help
就会出现以下信息
1 2 3 4 5 6 7 Usage: cmddemo.py [SWITCHES] args... Meta-switches: -h, --help Prints this help message and quits --help-all Print help messages of all subcommands and quit -v, --version Prints the program's version and quits
如果你想调用python3 cmddemo.py就显示这些帮助信息,可以在main中加入一句这样的代码:super().help()就可以了。
好的,帮助信息已经出来了,我们继续。
cli.Application类中为我们提供一些字段,可以直接显示信息,直接加上即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from plumbum import cli, colorsclass DemoApp (cli.Application) : PROGNAME = "kahn的工具包" | colors.green VERSION = "1.0" | colors.blue DESCRIPTION = "kahn的CLI集合" USAGE = "demo" def main (self, *args) : super().help() pass if __name__ == '__main__' : DemoApp.run()
我们再试一下命令,会输出一下信息
1 2 3 4 5 6 7 8 9 10 kahn的工具包 1.0 kahnCLI集合 Usage: demo Meta-switches: -h, --help Prints this help message and quits --help-all Print help messages of all subcommands and quit -v, --version Prints the program's version and quits
是不是有点感觉了?
代码里有颜色的输出设置,详细配置可见官网。也可以不设置颜色
主参数设置 现在试着添加一条参数
1 2 3 4 5 6 """上传下载命令集合""" @cli.autoswitch(str) def pull (self, param) : """从git上拉取源代码""" print("pull " + param)
上面的方法增加了一条主参数,我们再试一下命令,应该会显示
1 2 3 4 5 6 7 8 9 10 11 12 13 kahn的工具包 1.0 kahnCLI集合 Usage: demo Meta-switches: -h, --help Prints this help message and quits --help-all Print help messages of all subcommands and quit -v, --version Prints the program's version and quits Switches: --pull PARAM:str 从git上拉取源代码
这时候可以把main里面的help删掉了,不然输入什么都会打出帮助信息。
pull命令可以不带值,把pull的参数,和@cli.autoswitch的参数去掉就可以。–pull的注释可以通过python commen语法编写
子命令 我们在使用命令行的时候,一般会使用子命令进行操作。比如 git pull,git branch。像pull就是子命令
在同一个文件中添加一个类
1 2 3 4 5 6 7 8 9 10 11 12 @DemoApp.subcommand("sync") class KahnCmdSyncOpt (cli.Application) : """上传下载命令集合""" @cli.autoswitch() def pull (self) : """从git上拉取源代码""" pass @cli.autoswitch(str, argname="模块名") def upload (self, args) : """上传模块包。例:--upload=calendar""" pass
再去输入python3 cmddemo.py -h命令试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 kahn的工具包 1.0 kahnCLI集合 Usage: demo Meta-switches: -h, --help Prints this help message and quits --help-all Print help messages of all subcommands and quit -v, --version Prints the program's version and quits Switches: --pull PARAM:str 从git上拉取源代码 Subcommands: sync 上传下载命令集合; see 'kahn的工具包 sync --help' for more info
子命令的帮助信息也出来了。这想看子命令的参数,需要输入python3 cmddemo.py sync -h
这里我已经设置好了两个子命令,一个pull,一个upload,输入python3 cmddemo.py sync -h看一下效果
1 2 3 4 5 6 7 8 9 10 11 Usage: kahn的工具包 sync [SWITCHES] args... Meta-switches: -h, --help Prints this help message and quits --help-all Print help messages of all subcommands and quit -v, --version Prints the program's version and quits Switches: --pull 从git上拉取源代码 --upload 模块名:str 上传模块包。例:--upload=calendar
到这里基本设置已经ok了。
规划 在我规划的命令行工具的功能暂时先分两种
直接调用系统的shell命令执行
需要自己写逻辑的命令
先说一下1
调用系统命令 在python调用系统命令是很简单的,自行google就可以了,下面是代码示例
1 2 3 4 5 6 7 def executeSystemCmd (workDir, cmd) : print("executing...[%s]" % cmd) os.chdir(workDir) result = os.popen(cmd) for message in result.readlines(): print(message)
读取配置文件 我们需要一个配置文件存放写好的命令,这里可以使用Plumbum封装好的conf
1 2 3 4 5 6 with cli.Config('./conf/.myapp_rc' ) as conf: _confSection = conf['confSection' ] _sourceDir = conf['%s.sourceDir' % _confSection] _mainAppDir = conf['%s.mainAppDir' % _confSection] _package = conf['%s.package' % _confSection]
使用with cli.Config(‘./conf/.myapp_rc’) as conf:就可以从文件中读取一个标准的python配置文件,默认从[DEFAULT]这个section中读取。简单的说下python配置文件的样式
一般是这个样子
1 2 3 4 5 6 7 8 9 10 11 12 13 [DEFAULT] confSection = test1 [test1] package = com.test.kahn sourceDir = /Users/kahn/Dev/test mainAppDir = app [test2] package = com.test.kahn2 sourceDir = None mainAppDir = None
我们使用conf[test1.package]来读取[test1]中的值,不写test1.默认从DEFAULT中读取
用一个配置文件存放写好的命令
1 2 3 4 5 6 7 8 9 [DEFAULT] 0 = buck(testDebug);buck install -r appZroTestDebug 1 = buck(testRealese);buck install -r appZroTestRealese 2 = buck(productDebug);buck install -r appZroProductDebug 4 = 切换到最新分支;python xxx.py 5 = 输出数据库;adb pull /data/data/%%s/databases/ ~/ 6 = 获取当前页面名称;adb shell dumpsys activity | grep "mFocusedActivity" 7 = 清楚应用缓存;adb shell pm clear xxx 8 = 卸载xxx;adb uninstall xxx
接下来定义一个子命令cmd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @KahnUtils.subcommand("cmd") class KahnCmdPredefine (cli.Application) : """预定义命令集""" cmdDict = {} cmdDictHelp = {} with cli.Config('./conf/.myapp_rc_cmd_conf' ) as cmdConf: for key, option in cmdConf.parser.items("DEFAULT" ): optionArr = option.split(";" ) cmdDictHelp[key] = optionArr[0 ] cmdDict[key] = optionArr[1 ] @cli.switch( ["-h" , "--help" ], group = "Meta-switches" , help="Prints this help message and quits" ) def help (self) : super().help() print("可输入的命令:" ) for k,v in self.cmdDictHelp.items(): print("%s : %s" % (k, v)) print("示例:mt cmd 1" ) def main (self, *args) : for key, option in self.cmdDict.items(): if args.__len__() == 1 and args[0 ] == key: optionTemp = option if key == "5" : optionTemp = (option % _package) elif key == "13" : optionTemp = (option % _package) else : optionTemp = option executeSystemCmd(os.path.join(_sourceDir, _mainAppDir), optionTemp)
在这个子命令中,我先读取了所有已配置的命令,然后重写了它的help参数方法,把命令配置文件的命令输出到help中
以便让用户了解到,该命令都可以执行哪些功能。执行python3 cmddemo.py cmd -h后大致输出是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 预定义命令集 Usage: kahn的工具包 cmd [SWITCHES] args... Meta-switches: Prints this help message and quits -h, --help Prints this help message and quits --help-all Print help messages of all subcommands and quit -v, --version Prints the program's version and quits 可输入的命令: 0 : buck(testDebug) 1 : buck(testRealese) 2 : buck(productDebug) 3 : buck(productRealse) 4 : 切换到最新分支 5 : 输出数据库 6 : 获取当前页面名称 7 : 清楚应用缓存 8 : 卸载孕期 9 : 批量打aar包 10 : 小工具模块打包 11 : 查看tcp接口 12 : 导出数据库 13 : monkey压力测试
在这个类中,我的main方法接收了一个*args参数,然后判断参数的个数,最后找到相应的命令,调用刚刚写好的executeSystemCmd去执行它。
做一个示范:调用python cmddemo.py cmd 1
输出了
1 2 executing...[buck install -r appZroTestRealese] /bin/sh: buck: command not found
表示正在执行的命令。因为我系统没有buck命令,所以会输出/bin/sh: buck: command not found
再来调用一下python cmddemo.py -h看看输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 kahn的工具包 1.0 kahnCLI集合 Usage: demo Meta-switches: -h, --help Prints this help message and quits --help-all Print help messages of all subcommands and quit -v, --version Prints the program's version and quits Switches: --pull PARAM:str 从git上拉取源代码 Subcommands: cmd 预定义命令集; see 'kahn的工具包 cmd --help' for more info sync 上传下载命令集合; see 'kahn的工具包 sync --help' for more info
一个CLI的骨架就差不多搭好了。
安装 上面一个基本的命令行程序弄好了,那么怎么分发出去呢。总不能一直使用python cmddemo.py这种调用方式吧?而且执行的机器有可能没有安装Plumbum模块。
这里我们用到了setuptools模块。setuptools的使用方法,这里不展开讨论,大概配置是这样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from setuptools import setup setup( name='myutil', version='1.0', packages=['xxx'], include_package_data=True, install_requires=[ 'plumbum' ], entry_points=''' [console_scripts] myutil = myutil:cli ''', )
上面只是一个示例,详细的配置请自行google
有了setuptools,我们就可以分发出我们的python工具包了。
使用python setup.py install安装程序后。
你就可以在任何地方使用这样的命令调用了
就像你的adb一样
而不用再,非要找到你的源码路径,再去执行下面这样的命令
通过对setuptools的配置,它还可以帮你安装你所需要的依赖包
结尾 做为程序员的你,应该有一个自己的工具包
具体代码后面会更新在github上