系统管理模块 、 语法风格及布局 、 字符串详解
shutil 模块
基本概念
shutil
可以简单地理解为sh + util
,shell 工具 的意思shutil
模块是对os
模块的补充,主要针对文件的 拷贝、删除、移动、压缩和解压 操作
shutil 模块的主要方法
复制和移动
shutil.copyfileobj(fsrc, fdst)
将类似文件的对象 fsrc 的内容复制到类似文件的对象 fdst
copy 文件内容到另一个文件,可以 copy 指定大小的内容
这个方法是 shutil 模块中其它拷贝方法的基础,其它方法在本质上都是调用这个方法
让我们看一下它的源码:
def copyfileobj(fsrc, fdst, length=16*1024): while 1: buf = fsrc.read(length) if not buf: break fdst.write(buf)
代码很简单,一看就懂。但是要注意,其中的 fsrc,fdst 都是使用 open() 方法打开后的文件对象。
案例
>>> import shutil # 导入模块shutil,其中的copyfileobj()方法可以用于拷贝文件 >>> f1 = open('/etc/passwd','rb') # 打开原文件'/etc/passwd', 用于读取数据 >>> f2 = open('/tmp/mima', 'wb') # 打开目标文件'/tmp/mima', 用于写入数据 >>> shutil.copyfileobj(f1, f2) # 调用shutil模块的copyfileobj()方法,将f1中的内容拷贝到f2中 >>> f1.close() >>> f2.close() # 通过查看源文件和目标文件md5值,验证文件是否拷贝成功 [root@localhost xxx]# md5sum /etc/passwd /tmp/mima
shutil.copyfile(src, dst)
将名为 src 的文件的内容(无元数据)复制到名为 dst 的文件,然后返回 dst
同样看下它的源码,忽略前面一些检测用的代码,该方法的核心在最后几行,我们可以很清楚地看到
copyfile()
方法对copyfileobj()
进行了调用。
案例
>>> import shutil >>> shutil.copyfile("/etc/passwd", "/opt/passwd") '/opt/passwd'
shutil.copy(src, dst)
将文件 src 复制到文件或目录 dst,包括权限
src 和 dst 应为字符串。如果 dst 指定目录,则文件将使用 src 的基本文件名复制到 dst 中。返回新创建的文件的路径。
案例
>>> import shutil # 导入模块(如果已经导入,则不需要再次导入) >>> shutil.copy('/etc/hosts', '/tmp/zj.txt') # 拷贝hosts文件,到/tmp下,文件名为zj.txt [root@localhost xxx] # cat /tmp/zj.txts
源码
######### copy()调用了方法copyfile(),copyfile()调用了方法copyfileobj() ####### 方法copyfileobj()是最底层的代码,实现真实的数据拷贝过程 ### 第一步:【Ctrl + 鼠标左键】 ——》 点击copy()查看源码,源码如下: .......... def copy(src, dst, *, follow_symlinks=True): .......... if os.path.isdir(dst): dst = os.path.join(dst, os.path.basename(src)) copyfile(src, dst, follow_symlinks=follow_symlinks) copymode(src, dst, follow_symlinks=follow_symlinks) return dst .......... ###第二步:【Ctrl + 鼠标左键】 ——》 点击copyfile()查看源码,源码如下: .......... def copyfile(src, dst, *, follow_symlinks=True): .......... if not follow_symlinks and os.path.islink(src): os.symlink(os.readlink(src), dst) else: with open(src, 'rb') as fsrc: with open(dst, 'wb') as fdst: copyfileobj(fsrc, fdst) # copyfileobj()被copyfile()调用 return dst ..........
案例
>>> shutil.move('/tmp/zj2.txt', '/var/tmp') # 将/tmp目录下的文件移动到/var/tmp目录下 [root@localhost xxx]# ls /tmp/zj2.txt # 查看/tmp/zj2.txt,文件不存在 [root@localhost xxx]# ls /var/tmp/zj2.txt # 查看/var/tmp/zj2.txt ,文件移动成功
目录操作
shutil.copytree(src, dst)
递归地复制以 src 为根的整个目录树,返回目标目录。由 dst 命名的目标目录不能已经存在
案例
>>> shutil.copytree('/etc/security', '/tmp/security') # 使用copytree()时,需要指定一个不存在的目录,否则报错
shutil.rmtree(path)
删除整个目录树; **路径 **必须指向目录(而不是指向目录的符号链接)
>>> shutil.rmtree('/tmp/security')
权限管理
shutil.copymode(src, dst)
将权限位从 src 复制到 dst
文件内容,所有者和组不受影响
src 和 dst 是以字符串形式给出的路径名称
案例
# 使用shutil模块的copymode()功能【只拷贝权限】 [root@localhost xxx] # ll /tmp/zj.txt # 查看/tmp/zj.txt的权限,为:644 [root@localhost xxx] # ll /usr/bin/ls # 查看/usr/bin/ls的权限,为:755 [root@localhost xxx] # python # 进入python3的解释器交互界面 >>> shutil.copymode('/usr/bin/ls', '/tmp/zj.txt') # 将文件ls的权限,拷贝给文件zj.txt, zj.txt内容不变 [root@localhost xxx] # ll /tmp/zj.txt # 查看/tmp/zj.txt的权限,为:755,权限拷贝成功
shutil.copystat(src, dst)
将权限位,最后访问时间,上次修改时间和标志从 src 复制到 dst
shutil.chown(path, user=None, group=None)
更改给定 路径 的所有者 用户 和 组
案例
# 修改文件的所有者和所属组 [root@localhost xxx] # ll /tmp/zj.txt # 查看/tmp/zj.txt的属性,属主和属组均为root [root@localhost xxx] # python # 使用 shutil 模块的 chown() 的方法,修改文件的属主和属组为 'adm' >>> shutil.chown('/tmp/zj.txt', user='adm', group='adm') [root@localhost xxx] # ll /tmp/zj.txt # 属主和属组已经被更改为 adm
os 模块
对文件系统的访问大多通过 python 的 os 模块实现
该模块是 python 访问操作系统功能的主要接口
有些方法,如:copy 等,并没有提供,可以使用 shutil 模块作为补充
# os模块的常用方法 >>> import os#导入os系统模块 >>> os.#os.<Tab><Tab> 查看os模块的所以方法 >>> os.getcwd()#getcwd(),查看当前所处的文件路径,类似于: pwd >>> os.listdir()#listdir(), 查看当前目录下的所有文件(包括隐藏文件),类似于:ls -a >>> os.listdir('/tmp')#listdir('/tmp'), 查看/tmp目录下的内容,类似于:ls /tmp >>> os.mkdir('/tmp/mytest')#mkdir(), 创建目录,类似于:mkdir /tmp/mytest >>> os.mkdir('/tmp/demo/abc')#只能创建单级目录,父目录无法创建 >>> os.makedirs('/tmp/demo/abc')#创建目录时,父目录不存在,会自动创建,类似于: mkdir -p ... >>> os.chdir('/tmp/demo')#chdir(), 切换当前所处的文件位置,类似于:cd /tmp/demo >>> os.getcwd()#getcwd(),查看当前所处的文件路径,类似于: pwd >>> os.listdir()#listdir(), 查看当前目录下的所有文件(包括隐藏文件),类似于:ls -a >>> import shutil#导入shutil模块 >>> shutil.copy('/etc/hosts', '.')#copy(), 将/etc/hosts文件,拷贝到当前目录下 >>> os.listdir()#listdir(), 查看当前目录下的所有文件(包括隐藏文件),类似于:ls -a >>> os.symlink('/etc/passwd', 'mima')#symlink(), 为/etc/passwd建立软链接mima,类似于: ln -s /etc/passwd mima >>> os.listdir()#listdir(), 查看当前目录下的所有文件(包括隐藏文件),类似:ls -a [root@localhost day01]# ll /tmp/demo/hosts#重开终端【local(2)】,查看hosts文件的权限 >>> os.chmod('hosts', 0o600)#chmod(),修改文件hosts的权限,必须使用8进制数完成 >>> 0o644#使用10进制修改文件hosts的权限为644,先将8进制的644转换为十进制数 >>> os.chmod('hosts', 420)#使用转换后的十进制数修改修改hosts文件的权限 >>> os.remove('abc')#remove(), 只能删除单个文件,不能删除目录 >>> os.rmdir('abc')#rmdir(),只能删除空目录;要删除非空目录要使用shutil.rmtree() >>> os.rmdir('/var/tmp')#rmdir(),只能删除空目录;要删除非空目录要使用shutil.rmtree() >>> os.remove('hosts')#remove(),只能删除单个文件,不能删除目录 >>> os.unlink('mima')#unlink(),取消删除链接文件 >>> os.path.#查看os.path子模块的所有方法 >>> os.mkdir('abc')#mkdir(), 在当前路径下,创建一个目录'abc' >>> os.path.abspath('abc')#abspath(), 获取abc文件的路径 >>> os.path.basename('/tmp/demo/abc')#获取最右边'/',右边的数据‘abc’ >>> os.path.basename('/tmp/demo/abc/')#basename(),获取最右边'/',右边的数据'' >>> os.path.dirname('/tmp/demo/abc')#dirname(), 获取最右边'/',左边的数据'/tmp/demo' >>> os.path.split('/tmp/demo/abc')#split(), 路径切割,从最右边'/'开始,进行切割 >>> os.path.splitext('tedu.txt')#splitext(),将扩展名和文件名进行切割 >>> os.path.join('/tmp/demo', 'abc')#join(), 路径的拼接 >>> os.path.is#os.path.is<Tab><Tab>, 列出所有判断的方法 >>> os.path.isabs('tmp/abc/xyz')#'tmp/abc/xyz'是否是绝对路径,不管文件是否存在,False >>> os.path.isabs('/tmp/abc/xyz')#'/tmp/abc/xyz'是否是绝对路径,不管文件是否存在,True >>> os.path.isdir('/tmp/demo/abc')# 字符串是否为目录(文件必须存在,且必须是目录) ,True >>> os.path.isdir('/tmp/demo/xyz')# 字符串是否为目录(文件必须存在,且必须是目录),False >>> os.path.isfile('/etc/hosts')#字符串是否是文件(文件必须存在,且必须是文件),True >>> os.path.isfile('/etc/')#字符串是否是文件(文件必须存在,且必须是文件),False >>> os.path.islink('/etc/grub2.cfg')#字符串是否是链接文件(文件必须存在,且必须是链接文件),True >>> os.path.ismount('/')#判断字符串是否是挂载文件,'/'是挂载文件 >>> os.path.ismount('/etc')#判断字符串是否是挂载文件,'/etc' 不是挂载文件 >>> os.path.exists('/etc/hostname')#判断字符串是否存在,/etc/hostname,True
subprocess 模块
基础概念
subprocess 模块主要用于执行 系统命令
subprocess 模块允许你产生新的进程,连接到它们的 输入 / 输出 / 错误 管道,并获得它们的返回状态
通俗地说就是通过这个模块,你可以在 Python 的代码里执行操作系统级别的命令,比如 "ipconfig" 、"du -sh" 等等
run 方法
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, encoding=None, errors=None)
功能:执行 args 参数所表示的命令,等待命令结束,并返回一个 CompletedProcess 类型对象
>>> import subprocess >>> subprocess.run(['ls']) # 将命令写到列表中,执行linux命令 >>> subprocess.run(['ls', '/home']) # 将命令和操作的目录写到列表中,执行linux命令 >>> subprocess.run('ls /home') # 以字符串形式执行linux命令,无法区分命令和目标文件,错误,会把'ls /home 当做一个整体 >>> subprocess.run('ls /home', shell=True) # 若想以字符串形式执行,指定shell解释器
通过 shell 执行命令
subprocess.run(['ls', '/home'])
这种方式不支持环境变量,不支持命令的扩展,不支持 shell解释器 下执行命令,所以使用字符串的方式执行 linux命令
>>> subprocess.run('echo $HOME', shell=True) # 使用run()查看当前用户的家目录,使用shell执行命令 >>> subprocess.run('ls /root', shell=True)
run 方法返回值
run 方法查看上一级目录下的内容,使用 shell 执行命令
>>> subprocess.run('ls ..', shell=True) # 最后一行为返回值 day01 day02 day03 day04 CompletedProcess(args='ls ..', returncode=0) # run方法查看上一级目录下的内容,赋值给变量result >>> result = subprocess.run('ls ..', shell=True) day01 day02 day03 day04 # 查看变量result的值,为run()的返回值 >>> result CompletedProcess(args='ls ..', returncode=0) # 查看result中args列表的内容,为linux命令 >>> result.args 'ls ..' # 查看result中returncode的值,为0\;returncode 为状态码 # 上一条命令执行成功,结果为0;反之,结果为非零值 >>> result.returncode '0'
输出和错误
run 方法执行的结果默认打印在屏幕上,也可以通过管道将其存储在 标准输出和标准错误 中
# 将run方法执行的结果,保存在变量中 # run() 查看root的用户信息,查看zhangsan的用户信息 >>> result = subprocess.run('id root; id zhangsan', shell=True) # 将run() 的执行结果,赋值给result变量,stdout 记录的是标准输出信息;stderr 记录的是错误输出信息 >>> result = subprocess.run('id root; id zhangsan', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 查看result中的信息 >>> result >>> result.args# args列表中存放着用户执行的linux命令 >>> result.returncode # returncode为最后一条命令的执行状态 >>> result.stdout # stdout 记录的是命令的正确执行结果,以字节的方式显示 >>> result.stdout.decode() # 将linux命令的正确输出信息,转换成 string 类型 >>> result.stderr # stderr 记录的是命令的错误执行结果,以字节的方式显示
注意
subprocess 模块虽然可以支持所有的 linux 命令,但不可乱用,放弃其他的模块
subprocess 模块编写的代码,不具有跨平台性,不能在 windows,mac 等系统使用
练习 1:调用 ping 命令
需求
调用 ping 命令
编写 ping 函数
用于测试远程主机的联通性
ping 通显示:x.x.x.x:up
ping 不通显示:x.x.x.x:down
import sys # 导入sys模块,用于获取位置参数 import subprocess # 导入subprocess模块,用于执行linux的ping命令 def ping(host): # 使用def定义函数ping(),用于检测主机的联通性 # subprocess.run() 在shell解释器下,执行linux命令;ping -c 2 发送两个数据包; # %s 为占位符;&> /dev/null 将错误和正确信息都写入黑洞设备; # result 获得run()得放回信息,returncode为状态码(0 指命令成功;非0 指失败) result = subprocess.run('ping -c 2 %s &> /dev/null' % host, shell=True) if result.returncode == 0: return '%s:up' % host else: return '%s:down' % host # 测试代码块,__name__作为python文件调用时,执行该代码块 if __name__ == '__main__': host = sys.argv[1] print(ping(host))
异常处理
什么是异常
程序在运行时,如果 Python 解释器
遇到 到一个错误,会停止程序的执行,并且提示一些错误信息,这就是 异常
异常是因为程序出现了错误,而在正常控制流以外采取的行为
这个行为又分为两个阶段:
首先是引起异常发生的错误
然后是检测(和采取可能的措施)阶段
Python 中的异常
当程序运行时,因遇到未解的错误而导致中止运行,便会出现 traceback 消息,打印异常
KeyboardInterrupt # Ctrl + C,会产生用户中断执行错误 EOFError # Ctrl + D,会产出此错误
python 中异常演示
>>> a + 5# NameError,变量a没有定义 >>> 'hello'[5]# IndexError,字符串hello的最长索引下标为4 >>> a = 10 >>> if a = 10:# SyntaxError,python中的等于号使用'=='表示 >>> n = input('number:' )# 要求输入number时,Ctrl + D, 产生EOFError >>> n = input('number: ')# Ctrl + C,产生KeyboardInterrupt,用户中断执行
错误类型捕获
在程序执行时,可能会遇到 不同类型的异常,并且需要 针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
语法如下:
try: # 尝试执行的代码 pass except 错误类型1: # 针对错误类型1,对应的代码处理 pass except (错误类型2, 错误类型3): # 针对错误类型2 和 3,对应的代码处理 pass except Exception as result: print("未知错误 %s" % result)
try-except 语句
定义了进行异常监控的一段代码,并且提供了处理异常的机制
try: n = int(input('number: ')) # 没有输入任何值,回车,产生ValueError异常 print(n) except ValueError: print('无效的输入') # 当异常ValueError发生时,执行print()
带有多个 expect 的 try 语句
可以把多个 except 语句连接在一起,处理一个try 块中可能发生的多种异常
# 使用多个expect的try语句,处理异常
try: n = int(input('number: ')) # 没有输入任何值,回车,产生ValueError异常 print(n) except ValueError: # 当异常ValueError发生时,执行print() print('无效的输入') except KeyboardInterrupt: # Ctrl + C,产生KeyboardInterrupt,用户中断执行 print('\nBye-bye') except EOFError: # Ctrl + D, 产生EOFError, 没有内建输入 print('\nBye-bye')
检测上述模块中异常处理结果
[root@localhost xxx] # python day01.py number: # 回车,ValueError异常,s输入错误类型 无效的输入 [root@localhost xxx] # python day01.py number: ^CBye-bye # Ctrl + C,KeyboardInterrupt异常,用户操作中断 [root@localhost xxx]# python day01.py number: Bye-bye # Ctrl + D, EOFError异常, 没有内建输入
捕获未知错误
在开发时,要预判到所有可能出现的错误,还是有一定难度的
如果希望程序 无论出现任何错误,都不会因为
Python
解释器 抛出异常而被终止,可以再增加一个except
语法如下:
except Exception as result: print("未知错误 %s" % result)
异常参数
异常也可以有参数,异常引发后它会被传递给异常处理器
当异常被引发后参数是作为附加帮助信息传递给异常处理器的
查看异常提示信息
>>> n = int(input('number: ')) number: # 回车,ValueError异常 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: ''
使用多个expect的try语句,实现异常参数
try: n = int(input('number: ')) print(n) except ValueError as e: # 当异常ValueError发生时,将异常信息赋值给变量e print('无效的输入', e) # print(), 打印异常信息 except KeyboardInterrupt: print('\nBye-bye') except EOFError: print('\nBye-bye') [root@localhost day01]# python day01.py number: # 回车,ValueError异常 无效的输入 invalid literal for int() with base 10: '' # 输出异常信息
使用多个expect的try语句,捕获多个异常
try: n = int(input('number: ')) print(n) except ValueError as e: print('无效的输入', e) except (KeyboardInterrupt, EOFError): # 使用except捕获多个异常 print('\nBye-bye') [root@localhost day01]# python day01.py number: ^C # Ctrl + C,产生KeyboardInterrupt,用户中断执行 Bye-bye [root@localhost day01]# python day01.py number: # Ctrl + D, EOFError异常, 没有内建输入 Bye-bye
else 子句
在 try 范围中没有异常被检测到时,执行 else 子句
在else 范围中的任何代码运行前,try 中的所有代码必须完全成功
# else子句的使用 try: # 有可能发生异常的代码块 n = int(input('number: ')) except ValueError as e: # 当异常ValueError发生时,将异常信息赋值给变量e print('无效的输入', e) # print(), 打印异常信息 except (KeyboardInterrupt, EOFError): print('\nBye-bye') else: # 当不发生异常时,要执行的代码块 print(n) [root@localhost day01] # python day01.py number: 19 # 正常输入,打印else 19
finally子句
finally 子句是 无论异常是否发生,是否捕捉都会执行的一段代码
如果打开文件后,因为发生异常导致文件没有关闭,可能会发生数据损坏,使用finally 可以保证文件总是能正常的关闭
# finally子句的使用 try: # 有可能发生异常的代码块 n = int(input('number: ')) except ValueError as e: # 当异常ValueError发生时,将异常信息赋值给变量e print('无效的输入', e) # print(), 打印异常信息 except (KeyboardInterrupt, EOFError): print('\nBye-bye') exit() # 退出程序 else: # 当不发生异常时,要执行的代码块 print(n) finally: # 不管异常是否发生都会执行代码块 print('Done') [root@localhost day01]# python day01.py number: # Ctrl + D, EOFError异常, 没有内建输入 Bye-bye Done # 出现异常,finally 继续执行 [root@localhost day01]# python day01.py number: 19 # 正常输入整数,打印结果 19 Done # 没有出现异常,finally 还是继续执行
练习 3:简化除法判断
需求
提示用户输入一个数字作为除法
如果用户按下 Ctrl + C 或 Ctrl + D 则退出程序
如果用户输入非数字字符,提示用户应该输入数字
如果用户输入 0,提示用户 0 不能作为除法
# mydiv.py try: # 有可能发生异常的代码块 n = int(input('number: ')) result = 100 / n # 当异常ValueError发生时,将异常信息赋值给变量e except (ValueError, ZeroDivisionError) as e: print('无效的输入', e) # print(), 打印异常信息 except (KeyboardInterrupt, EOFError): # 出现异常,执行print(),exit() print('\nBye-bye') exit() # 退出程序 else: # 当不发生异常时,要执行的代码块 print(result) finally: # 不管异常是否发生都会执行代码块 print('Done') [root@localhost day01] # python mydiv.py number: # 空值,回车,异常处理 无效的输入 invalid literal for int() with base 10: '' Done # 不管异常是否发生,都会打印 # 使用python解释器,执行day01.py [root@localhost day01]# python mydiv.py number: 0 # 输入0,异常处理 无效的输入 division by zero Done # 不管异常是否发生,都会打印
自定义异常
抛出异常—raise
应用场景
在开发中,除了 代码执行出错
Python
解释器会 抛出 异常之外还可以根据 应用程序 特有的业务需求 主动抛出异常
示例
提示用户 输入密码,如果 长度少于 8,抛出 异常
注意
当前函数 只负责 提示用户输入密码,如果 密码长度不正确,需要其他的函数进行额外处理
因此可以 抛出异常,由其他需要处理的函数 捕获异常
抛出异常
Python
中提供了一个Exception
异常类在开发时,如果满足 特定业务需求时,希望 抛出异常,可以:
创建 一个
Exception
的 对象使用
raise
关键字 抛出 异常对象
需求
定义
input_password
函数,提示用户输入密码如果用户输入长度 < 8,抛出异常
如果用户输入长度 >= 8,返回输入的密码
def input_password(): # 1. 提示用户输入密码 pwd = input("请输入密码:") # 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码 if len(pwd) >= 8: return pwd # 3. 密码长度不够,需要抛出异常 # 1> 创建异常对象 - 使用异常的错误信息字符串作为参数 ex = Exception("密码长度不够") # 2> 抛出异常对象 raise ex try: user_pwd = input_password() print(user_pwd) except Exception as result: print("发现错误:%s" % result)
练习 4:自定义异常
需求
编写第一个函数,接收姓名和年龄,如果年龄不在1到120之间,产生 ValueError 异常
# 编写函数get_info(),在文件user_info.py上操作 def get_info(name, age): # 定义函数,接收姓名和年龄 # 异常触发ValueError,提示信息为:无效的年龄(1 ~ 119) if not 0 < age < 120: raise ValueError('无效的年龄(1 ~ 119)') # 当age在0 ~ 120的范围,else代码块执行 else: print('%s is %s years old' %(name, age)) # 调用函数get_info(),在文件call_user_info.py上操作 # 将user_info.py作为模块导入 import user_info try: # 有可能发生异常的代码块 user_info.get_info('tom', 200) except ValueError as e: # 当异常ValueError发生时,将异常信息赋值给变量e print('Error:', e) # print(), 打印异常信息 exit() # 退出程序,不继续执行后续代码 print('Done') # exit(),退出后,此代码不执行