Python自动化运维之9、模块之sys、os、hashlib、random、time&datetime、logging、subprocess

时间:2021-05-23 04:08:46

python模块

  用一砣代码实现了某个功能的代码集合。 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合。而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。模块分为内建模块、自定义的模块、安装的第三方的模块

导入模块

Python之所以应用越来越广泛,在一定程度上也依赖于其为程序员提供了大量的模块以供使用,如果想要使用模块,则需要导入。导入模块有一下几种方法:

import module
from module.xx.xx import xx
from module.xx.xx import xx as rename
from module.xx.xx import *

导入模块其实就是告诉Python解释器去解释那个py文件

  • 导入一个py文件,解释器解释该py文件
  • 导入一个包,解释器解释该包下的 __init__.py 文件

那么问题来了,导入模块时是根据那个路径作为基准来进行的呢?即:sys.path

>>> import sys
>>> print(sys.path)
['', '/home/tomcat/.pyenv/versions/3.5.1/lib/python35.zip',
'/home/tomcat/.pyenv/versions/3.5.1/lib/python3.5',
'/home/tomcat/.pyenv/versions/3.5.1/lib/python3.5/plat-linux',
'/home/tomcat/.pyenv/versions/3.5.1/lib/python3.5/lib-dynload',
'/home/tomcat/.pyenv/versions/3.5.1/lib/python3.5/site-packages']

如果sys.path路径列表没有你想要的路径,可以通过 sys.path.append('路径') 添加。

import sys
import os
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(project_path) 

常用内置模块

内置模块是Python自带的功能,在使用内置模块相应的功能时,需要【先导入】再【使用】

一、sys

用于提供对Python解释器相关的操作:

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时exit(0)
sys.version 获取Python解释程序的版本信息
sys.maxint 最大的Int值
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
sys.stdin 输入相关
sys.stdout 输出相关
sys.stderror 错误相关

进度条示例:  

import sys
import time def view_bar(num,total):
rate = num / total
rate_num = int(rate * 100)
r = '\r%s%d%%' % (">"*num,rate_num)
sys.stdout.write(r)
sys.stdout.flush() if __name__ == '__main__': for i in range(0, 100):
time.sleep(0.1)
view_bar(i, 100)

二、os

用于提供系统级别的操作:

os.getcwd()                 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd
os.curdir 返回当前目录: ('.')
os.pardir 获取当前目录的父目录字符串名:('..')
os.makedirs('dir1/dir2') 可生成多层递归目录
os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove() 删除一个文件
os.rename("oldname","new") 重命名文件/目录
os.stat('path/filename') 获取文件/目录信息
os.sep 操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep 当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep 用于分割文件路径的字符串
os.name 字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command") 运行shell命令,直接显示,不能保存执行结果
os.popen("bash command").read() 运行shell命令,可以保存执行结果
os.environ 获取系统环境变量
os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) 如果path是绝对路径,返回True
os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间

三、hashlib

用于加密相关的操作,代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法

import hashlib

# ######## md5 ########
hash = hashlib.md5()
# help(hash.update)
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
print(hash.digest()) ######## sha1 ######## hash = hashlib.sha1()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest()) # ######## sha256 ######## hash = hashlib.sha256()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest()) # ######## sha384 ######## hash = hashlib.sha384()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest()) # ######## sha512 ######## hash = hashlib.sha512()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())

以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。  

import hashlib

# ######## md5 ########

hash = hashlib.md5(bytes('898oaFs09f',encoding="utf-8"))
hash.update(bytes('admin',encoding="utf-8"))
print(hash.hexdigest())

四、random

import random

print(random.random())              # 生成0-1之间的随机小数
print(random.randint(1, 20)) #生成1到20的整数包括20
print random.uniform(10, 20) #生成10到20之间的浮点数
print(random.randrange(1, 10)) #生成1到10的证书不包括10,第3个参数可以指定步长
print(random.choice(["JGood", "is", "a", "handsome", "boy"])) # 从序列中随机选一个数 # 每次对序列随机排序
p = ["Python", "is", "powerful", "simple"]
random.shuffle(p)
print(p)

随机验证码

import random

li = []
for i in range(6):
r = random.randint(0, 4)
if r == 2 or r == 4:
num = random.randrange(0, 10)
li.append(str(num))
else:
temp = random.randrange(65,91)
c = chr(temp)
li.append(c) result = "".join(li)
print(result)
一些例子

五、time&datetime

时间相关的操作,时间有三种表示方式:

  • 时间戳                1970年1月1日之后的秒,即:time.time()
  • 格式化的字符串    2014-11-11 11:11,    即:time.strftime('%Y-%m-%d')
  • 结构化时间          元组包含了:年、日、星期等... time.struct_time    即:time.localtime()

time

import time

print(time.clock())    #返回处理器时间,3.3开始已废弃
print(time.process_time()) #返回处理器时间,3.3开始已废弃
print(time.time()) #返回当前系统时间戳输出:1471161757.5214906
print(time.ctime()) #输出字符串格式时间:Sun Aug 14 16:04:02 2016 ,当前系统时间
print(time.ctime(time.time()-86640)) #将时间戳转为字符串格式
print(time.gmtime()) #获取结构化时间
print(time.gmtime(time.time()-86640)) #将时间戳转换成struct_time格式
print(time.localtime(time.time()-86640)) #将时间戳转换成struct_time格式,但返回的本地时间
print(time.mktime(time.localtime())) #与time.localtime()功能相反,将struct_time格式转回成时间戳格式
time.sleep(4) #睡上4秒
print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将struct_time格式转成指定的字符串格式
print(time.strptime("2016-01-28","%Y-%m-%d") ) #将字符串格式转换成struct_time格式

datetime

import datetime

print(datetime.date.today()) #输出格式 2016-01-26
print(datetime.date.fromtimestamp(time.time()-864400) ) #2016-01-16 将时间戳转成日期格式
current_time = datetime.datetime.now() #
print(current_time) #输出2016-01-26 19:04:30.335935
print(current_time.timetuple()) #返回struct_time格式 #datetime.replace([year[, month[, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]]]])
print(current_time.replace(2014,9,12)) #输出2014-09-12 19:06:24.074900,返回当前时间,但指定的值将被替换 str_to_date = datetime.datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M") #将字符串转换成日期格式
new_date = datetime.datetime.now() + datetime.timedelta(days=10) #比现在加10天
new_date = datetime.datetime.now() + datetime.timedelta(days=-10) #比现在减10天
new_date = datetime.datetime.now() + datetime.timedelta(hours=-10) #比现在减10小时
new_date = datetime.datetime.now() + datetime.timedelta(seconds=120) #比现在+120s
print(new_date)

六、logging模块  

很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()info()warning()error() and critical() 5个级别,下面我们看一下怎么用。

日志级别对应的数字:

CRITICAL = 50   ERROR = 40   WARNING = 30   INFO = 20   DEBUG = 10   NOTSET = 0

日志记录格式:

%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息

1、单文件日志

import logging

logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
filename='test.log',
filemode='w') logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

2、多文件日志

对于上述记录日志的功能,只能将日志记录在单文件中,如果想要设置多个日志文件,logging.basicConfig将无法完成,需要自定义文件和日志操作对象。

#!/usr/bin/env python
# -*- coding:utf-8 -*- import logging #获取日志器对象
logger = logging.getLogger('TEST-LOG') # 返回一个logger对象,没有指定的话默认是root logger
logger.setLevel(logging.DEBUG) #设置全局日志级别 #定义屏幕控制器把日志输出屏幕
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG) # 输出屏幕的日志级别
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter) # 给输出屏幕的日志设置日志格式
logger.addHandler(ch) # 将设定好的控制器添加到日志器中 #定义文件控制器把日志输出文件
fh = logging.FileHandler("access.log")
fh.setLevel(logging.WARNING) #设置输出文件的日志级别
formatter1 = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter1) # 给输出文件的日志设置日志格式
logger.addHandler(fh) # 将设定好的控制器添加到日志器中 logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

注意:局部日志级别只有大于全局日志级别才会记录  

七、subprocess模块   

可以执行shell命令的相关模块和函数有:

  • os.system()   直接使用shell命令,不能保存运行的结果
  • os.spawn*
  • os.popen().read()  直接使用shell命令,可以保存运行的结果
  • commands.*  python3.x已经废除
  • subprocess

1、os.system()

>>> import os
>>> os.system('ls')
a.py b.py Desktop myenv35 PycharmProjects workplace
a.txt b.txt myenv27 pass.py scripts
0

2、os.popen().read()  

>>> import os
>>> fi = os.popen('ls').read()
>>> print(fi)
a.py
a.txt
b.py
b.txt

3、commands(在python2.x中执行)

>>> import commands
>>> result = commands.getoutput('ls')
>>> print(result)
a.py
a.txt
b.py
b.txt >>> result1 = commands.getstatusoutput('ls')
>>> print(result1)
(0, 'a.py\na.txt\nb.py\nb.txt\nDesktop\nmyenv27\nmyenv35\npass.py\nPycharmProjects\nscripts\nworkplace')

4、subprocess  

subprocess.call()  执行命令,返回状态码,shell=False,第一个参数必须是列表,shell=True,第一个参数就直接输入命令即可

>>> ret = subprocess.call(["ls", "-l"], shell=False)
或:
>>> ret = subprocess.call("ls -l", shell=True)

subprocess.check_call() 执行命令,如果执行状态码是 0 ,则返回0,否则抛异常

>>> ret = subprocess.check_call(["ls", "-l"],shell=False)
>>> print(ret)
>>> ret = subprocess.check_call("exit 1", shell=True)
>>> print(ret)

subprocess.check_output() 执行命令,如果状态码是 0 ,则返回执行结果,否则抛异常,注意这里返回的是字节类型,需要转换

>>> ret = subprocess.check_output(["echo", "Hello World!"],shell=False)
>>> print(str(ret,encoding='utf-8'))

>>> ret = subprocess.check_output("exit 1", shell=True)
>>> print(str(ret,encoding='utf-8'))

subprocess.run()  python3.5新加的功能,代替os.system,os.spawn

>>> import subprocess

>>> subprocess.run(["ls", "-l"])
total 56
-rw-rw-r-- 1 tomcat tomcat 61 8月 11 23:27 a.py
-rw-rw-r-- 1 tomcat tomcat 12929 8月 8 18:03 a.txt
CompletedProcess(args=['ls', '-l'], returncode=0) >>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0, stdout=b'crw-rw-rw- 1 root root 1, 3 8\xe6\x9c\x88 11 09:27 /dev/null\n')

subprocess.Popen()  用于执行复杂的系统命令,是上面方法的底层实现

调用subprocess.run(...)是推荐的常用方法,在大多数情况下能满足需求,但如果你可能需要进行一些复杂的与系统的交互的话,你还可以用subprocess.Popen(),语法如下:

p = subprocess.Popen("find ~/ -size +1000 -exec ls -shl {} \;",shell=True,stdout=subprocess.PIPE)
print(p.stdout.read())

参数:

  • args:shell命令,可以是字符串或者序列类型(如:list,元组)
  • bufsize:指定缓冲。0 无缓冲,1 行缓冲,其他 缓冲区大小,负值 系统缓冲
  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
  • close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。
  • 所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。
  • shell:同上
  • cwd:用于设置子进程的当前目录,相当于shell中cd进入当前目录
  • env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
  • universal_newlines:不同系统的换行符不同,True -> 同意使用 \n
  • startupinfo与createionflags只在windows下有效
  • 将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等

终端输入的命令分为两种:

  • 输入即可得到输出,如:ifconfig
  • 输入进行某环境,依赖再输入,如:python

(1)输入即可得到输出  

import subprocess

obj = subprocess.Popen(["mkdir","test"],cwd='/tmp/',)
或者
obj1 = subprocess.Popen("mkdir test1", shell=True, cwd='/tmp/',)

(2)输入进行某环境,依赖再输入,通过输入,输出,错误管道,输入数据,获取数据

import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)")
obj.stdin.close() cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read()
obj.stderr.close() print(cmd_out)
print(cmd_error) 或者
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)") out_error_list = obj.communicate()
print(out_error_list) 或者
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out_error_list = obj.communicate('print("hello")')
print(out_error_list)