使用 Python 编写脚本并发布
使用 Python 编写脚本并发布
P1: 脚本
通常在 Linux 服务器上会遇到在命令行中输入命令的操作,而有些操作包含的命令数目较多或者其中的命令包含的参数较多,如果一个一个的敲命令的话就太麻烦了,有几种做法可以简化操作:
- 使用 alias 为命令编写别名,比如我之前开发一个网站程序 minor-sspymgr 时,经常需要上传修改后的代码,更新服务器上的代码,重启网站程序。为了方便,我定义一个 alias 别名命令:
alias updateMgr='cd ~/minor-sspymgr/ && git pull origin master && pm2 restart sspymgr'
这种方法的优点是很方便简单,把常用的命令组合到一起,用一个别名表示即可使用。当然,它的缺点也很明显,就是不能执行一些复杂的命令,而且难以将参数应用到命令中。
- 编写一个 .sh 脚本,我之前写简单脚本时用的都是 bash,对于上述例子,用 sh 脚本编写起来也很类似,可以简单的把上述命令以
&&拆分为 3 行:
#!/bin/bash
cd ~/minor-sspymgr/
git pull origin master
pm2 restart sspymgr
当然了,实际的脚本远非如此简单,可以用 $1 ... $n 获取命令行参数,执行一些更复杂的逻辑。
为什么用 Python 写脚本
既然用 bash 已经可以编写一些脚本了,那么为什么我还要用 Python 编写脚本呢?原因有两个:1. bash 用的不多,学的也早,而且 bash 和常用的编程语言的语法有些差别,很多内容都容易忘记,用 Python 写的话也可以熟悉 Python(尽管我是从 flask 制作一个 WSGIServer 开始使用 Python 的),2. Python 的库很多也很方便使用,编写脚本给人一种流畅的感觉。当然最主要的原因就是因为 Python 里面有 argparse 这个库,对于解析命令行参数来说十分方便。
如何解析命令行参数
bash 脚本里面获取命令行参数的方式很简单,但是解析起来却比较麻烦,如果有 bash 解析命令行参数的库,希望可以推荐给我。不过即使有这样的库,估计我还是会选择 Python 来编写脚本了。在 Python 里解析命令行参数的模块比较多,有 getopt、optparse、argparse 等 1。目前我只用过 argparse,因此这里也只会涉及到如何用 argparse 来解析命令行参数。至于前两个模块,如果有兴趣的话,可以自行了解。argparse 使用起来非常简单:
from argparse import ArgumentParser
parser = ArgumentParser(description="""Run this script under SUPER USER privilege. Create or remove git repository
under direcotry: {}. Currently only works for Linux. Most operations are only tested under Linux.
Script version : {}.
""".format(config["repo_dir"], __version__))
parser.add_argument("-V", "--version", help="Print this script version", action="store_true")
args = parser.parse_args()
if args.version:
print("Script version is {}".format(__version__))
上面这几行代码就做好了一个简单的命令行解析功能,当我们输入 script.py -V 的时候就会打印出脚本的版本。不需要在意命令行参数的位置或者是否必须要加上这个参数,只需要将注意力放在代码的逻辑上即可,argparse 这个模块可以极大的方便我们解析命令行参数。
P2: 示例:初始化 git 仓库的脚本
之前用 git 搭建过源代码管理服务器 2、3。如果要在这样的服务器上添加一个共享仓库,就需要在仓库根目录下面执行一些操作了。比如说,我的 git 仓库存放的目录是 /src 这个目录下面的所有文件及文件夹的属主和属组都是 git,要想新建一个共享仓库,最开始我的做法是:
- cd /src
- mkdir newrepo && cd newrepo
- git init --bare --shared
- cd ..
- sudo chown -R git:git newrepo && sudo chmod -R g+rwx newrepo
步骤还是有点多的,每次都要敲这么多命令很麻烦,把这些命令写到 bash 脚本中,比如叫做 gitrepo.sh,给它加上 x 执行权限,在终端里输入 sudo gitrepo.sh reponame 就能完成上面的操作。
用 Bash 编写的脚本
Bash 脚本,编写简单,就是把在命令行中敲过的命令依次写到文件中即可,但是对于不常写 bash 脚本的我来说,要编写一个完备的脚本,并且要包含参数、异常处理等逻辑来说比较麻烦:
#!/bin/bash
# filename: gitrepo.sh
# @author BriFuture
# @details create bare and shared repository within /src directory
repoName=$1
if [ -z "$repoName" ]; then
echo "Repo is empty!"
exit 1
fi
dotpos=`expr index "$repoName" "."`
if [ "$dotpos" -gt 0 ]; then
echo "found";
else
repoName=$repoName".git"
fi
# go to the repository dir
cd /src
mkdir $repoName
cd $repoName
echo "Init git repo in $repoName"
git init --bare --shared
# change the own of repository
cd ../
sudo chown -R git:git $repoName
sudo chmod -R g+rwx $repoName
用 Python 编写的脚本
实际上,我用 Python 重新编写这个脚本时,在参数中加入了更多的可选项,此前的 bash 脚本只能够添加仓库,这个全新的脚本加入了其他操作:添加新的仓库,列出所有仓库,删除一个仓库。并且完善了命令行的帮助信息,为脚本添加了配置文件以及输出日志。
另外在执行脚本的过程中需要用到超级用户的权限,因此在脚本中添加了检查当前用户权限的功能:
def is_root():
if hasattr(os, "getuid"):
return os.getuid() == 0
return False
该 Python 脚本的主要功能是添加和删除仓库(目录),添加仓库的操作如下,分别是创建目录,执行 git init --bare --shared 命令,更改文件属主和属组的操作(实际的脚本中包含一些日志记录和异常处理的代码):
from pathlib import Path
def createRepo(repo: Path):
if repo.exists():
return
repo.mkdir(parents=True)
subprocess.run(["git", "init", "--bare", "--shared", str(repo.absolute())])
shutil.chown(repo, config["user"], config["group"])
删除仓库的操作更加简单,调用 shutil 递归删除文件夹即可,不能使用 repo.rmdir() 函数,因为一般仓库文件夹是非空的:
import shutil
def deleteRepo(repo: Path):
if not repo.exists():
return
shutil.rmtree(str(repo))
罗列所有仓库使用 Path.iterdir() 函数即可。
这样一个可以使用的脚本就制作完成了,如果只是单纯的编写一个脚本的话,可以在文件的开头加上 #!/usr/bin/python3 这样的标记,表明这是一个可执行的 Python3 脚本,在 linux 系统上给它加上 x 权限,比如这个文件的名称为 gitrepo.py,我们可以在终端中输入 gitrepo.py -h 或者 python3 gitrepo.py -h 查看这个命令的帮助信息。
详细的代码可以在 Github 上的 gitrepo.py 文件中查看。
P3: 以 wheel 包的形式发布
前面说过,我们可以把写好的 Python 文件当做脚本执行,但是每次都要敲 gitrepo.py -h 这样的命令,能不能就像平时用 ls 这些命令一样直接敲 gitrepo -h 呢?最简单的是用 ln 建立软链接:sudo ln -s /path/to/gitrepo.py /usr/bin/gitrepo,但是这里我们可以借助 pypi 发布我们已经写好的命令,将我们的脚本发布到 pypi 上,那么还可以在没有该脚本的机器上利用 pip 进行安装。
以 brifuture-facilities 这个项目为例,在项目的根目录下面新建一个 setup.py 文件,输入以下内容:
# -*- coding: utf-8 -*-
from setuptools import setup, find_packages
with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
with open('requirements.txt', "r", encoding="utf-8") as f:
requires = f.read().splitlines()
from myfacilities import __version__
setup(
name = "brifuture-facilities",
packages = find_packages(where='.'),
version = __version__,
entry_points = {
"console_scripts": [
'bf_broadcast = myfacilities.broadcast:main',
'bf_gitrepo = myfacilities.gitrepo:main',
]
},
description = "BriFuture's scripts set, all scripts will be written with Python3",
author = "BriFuture",
author_email = "jw.brifuture@gmail.com",
license = "GPLv3",
url = "http://github.com/brifuture/",
install_requires = requires,
include_package_data = True,
zip_safe=True,
exclude_package_data = {'': ['__pycache__']},
# download_url = "",
keywords = [ "webserver", "socks-manager" ],
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3" ,
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent"
],
long_description = long_description,
long_description_content_type="text/markdown",
)
我们需要利用的是 setuptools,从这个模块中导入 setup,它的参数都很直观,我们想要添加一个可执行的脚本时,在 entry_points 关键字参数中添加即可,如:
entry_points = {
"console_scripts": [
'bf_broadcast = myfacilities.broadcast:main',
'bf_gitrepo = myfacilities.gitrepo:main',
]
},
由于这个项目中的文件是包的一部分,所以我没法直接使用 python 运行其中的某个脚本,像 python3 myfacilities.gitrepo -h 是无法运行的,但是借助于 setuptools,我们可以将写好的程序安装到机器中,python3 setup.py install 可以通过 setup.py 文件进行安装,也可以使用 pip install . 进行安装,详细的教程可以在 文档 中找到。
接下来制作 wheel 包,命令很简单:
python3 setup.py sdist bdist_wheel
这样会将我们的代码打包到 dist/ 目录下。
我们需要将制作好的程序发布到 pypi 上,安装好 twine: pip install twine, 在 $HOME 目录(linux) 或者 C:\Users\yourname\ 目录(windows)下新建一个 .pypirc 文件,输入下面的内容:
[distutils]
index-servers=
pypi
testpypi
[pypi]
repository: https://upload.pypi.org/legacy/
username: yourname
password: yourpasswd
[testpypi]
repository: https://test.pypi.org/legacy/
username: yourname
password: yourpasswd
接下来就可以发布了,但是不要着急,正式发布前都请将制作好的包发布到 testpypi 上:
python3 -m twine upload -r testpypi dist/* --skip-existing
等你确认一切无误后,在将 -r testpypi 参数换成 -r pypi 以便正式发布。
P4: 小结
尽管制作一个小巧、实用而且完备的 Python 脚本很有意思,但是要想做出一个实用、对用户友好的脚本,还是需要花一些时间的。实际上如果要为用户提供更加友好的实用方式,可以考虑以界面的形式为用户提供操作,比如说以网页的形式提供操作,在服务器后台对用户操作进行处理。不过你也可能注意到了,这个脚本的功能其实就是很基础的增删查改。
上面示例的 Python 脚本在使用过程中会用到超级用户权限,为了防止操作失败,在添加、删除仓库时必须提供超级用户权限,否则脚本将会退出,还有一种方式可以在运行时获得超级用户权限,就是使用 [elevate][https://github.com/barneygale/elevate] 进行提权,具体的使用方式还没来得及看,不过我猜测应该需要向 elevate 提供一个超级用户密码的文件。
如果你想仔细查看上述 Python 示例代码的话,可以在 Github 上找到 brifuture-facilities 项目的代码。如果你觉得我的文章或者其中的代码对你有帮助,请给它点个赞。
参考
掘金:Python中最好用的命令行参数解析工具
cnblogs:搭建简单的Git服务器
cnblogs:lighttpd 与 gitweb 搭建服务器
setuptools documentation
使用 Python 编写脚本并发布的更多相关文章
- python编写脚本应用实例
这里主要记录工作中应用python编写脚本的实例.由于shell脚本操作数据库(增.删.改.查)并不是十分直观方便,故这里采用python监控mysql状态,然后将状态保存到数据库中,供前台页面进行调 ...
- 【MonkeyRunner】[技术博客]用python编写脚本查看设备信息
[MonkeyRunner]用python编写脚本查看设备信息 原以为是个简单的操作,在实践的时候发现了一些问题. python脚本 test.py: from com.android.monkeyr ...
- python编写脚本,删除固定用户下的所有表
脚本如下: [oracle@ycr python]$ more t_del.py #/usr/bin/python#coding:utf8 import sysimport cx_Oracle i=0 ...
- Zabbix调用外部脚本发送邮件:python编写脚本
Zabbix调用外部脚本发送邮件的时候,会在命令行传入两个参数,第一个参数就是要发送给哪个邮箱地址,第二个参数就是邮件信息,为了保证可以传入多个参数,所以假设有多个参数传入 #!/usr/bin/en ...
- Python编写脚本(输出三星形状的‘*’符号)
环境:python3.* 心得:个人认为脚本非我强项,以下效果可以有更简单解决方案,纯属练习逻辑. 方案一: s=1 while s<=10: #这是决定多少列,起始为1,大循环一圈即加一,就是 ...
- python编写脚本
#!/usr/bin/env python #-*- coding:utf-8 -*- import sys import os from subprocess import Popen,PIPE c ...
- python编写脚本,登录Github通过指定仓库指定敏感关键字搜索自动化截图生成文件【完美截图】
前言:为了避免开发人员将敏感信息写入文件传到github,所以测试人员需要检查每个仓库是否有写入,人工搜索审核比较繁琐,所以写一个脚本通过配置 配置文件,指定需要搜索的仓库和每个仓库需要搜索的关键字, ...
- python编写网络抓包分析脚本
python编写网络抓包分析脚本 写网络抓包分析脚本,一个称手的sniffer工具是必不可少的,我习惯用Ethereal,简单,易用,基于winpcap的一个开源的软件 Ethereal自带许多协议的 ...
- python编写文件统计脚本
python编写文件统计脚本 思路:用os模块中的一些函数(os.listdir().os.path.isdir().os.path.join().os.path.abspath()等) 实现功能:显 ...
随机推荐
- VR与AR的发展趋势分析
概要 你是否想象过与神秘的深海生物近距离接触?你是否梦想过穿戴钢铁侠那样的超先进科技装备成为超级英雄?你又是否幻想过与梦中的女神面对面的交流?这些可能在以前都只能是存在于脑海中的幻想,可是在如今有一项 ...
- C#中字段、属性、只读、构造函数赋值、反射赋值的相关
C#中字段.属性和构造函数赋值的问题 提出问题 首先提出几个问题: 1.如何实现自己的注入框架? 2.字段和自动属性的区别是什么? 3.字段和自动属性声明时的直接赋值和构造函数赋值有什么区别? 4.为 ...
- C# Using 开发随录
Using 关键字有2个主要用途: 1.做为语句 用于定义一个范围,在此范围的末尾将释放对象 2.做为指令 用于为命名空间创建别名或导入其他命名空间中定义的类型 C# 通过 .NET Framew ...
- iOS 安装包瘦身 (上篇)
本文来自网易云社区 作者:饶梦云 1. 安装包组成 谈到 App 瘦身,最直接的想法莫过于分析一个安装包内部结构,了解其每一部分的来源.解压一个 ipa 包,拿到其 payload 中 app 文件的 ...
- TestNG学习笔记目录
学习TestNG主要用于GUI自动化测试使用,学习目录随进度不断更新.文档内容主要是翻译官方doc,同时加入自己的理解和案例.如有理解偏差欢迎指正 一.TestNG Eclipse plug-in 安 ...
- kolla-ansible 重新部署 ceph-mon 组件
1.备份数据 [root@controller ~]# mv /var/lib/docker/volumes/ceph_mon /var/lib/docker/volumes/ceph_backup/ ...
- yum 下载RPM包而不进行安装
yum命令本身就可以用来下载一个RPM包,标准的yum命令提供了--downloadonly(只下载)的选项来达到这个目的. $ sudo yum install --downloadonly < ...
- django系列6--Ajax06 使用插件,Sweet-Alert插件
使用SweetAlert插件 GitHub上的下载链接 下载完成后放入django项目静态目录下,在html文件中引入静态文件,下面是script部分 $(".btn-danger" ...
- J - Judge(快速幂)(同余定理)
J - Judge Time Limit:1000MS Memory Limit:131072KB 64bit IO Format:%lld & %llu Submit S ...
- 初识Mybatis框架
mybatis框架 主要是对数据库进行操作的 编写sql语句 使我们对数据库的crud操作更加简洁方便!! 1.使用mybatis框架 进行第一个项目 查询数据库 并返回数据 :(简单) (1)搭建 ...