一、操作系统接口
os模块提供了很多与操作系统相关联的函数;
导入os模块时建议使用 "import os" 风格而非 "from os import *"。原因是可以保证随操作系统变化而有所变化的 os.open() 函数不会覆盖内置函数 open();
内置的 dir() 和 help() 函数对使用 os 等大型模块时非常有用,dir()函数列出os模块的所有方法,help()方法用于说明os模块方法的使用;
Python 中使用 shutil 模块可实现文件或目录的复制、删除、移动;
二、shutil 模块实现文件或目录的复制、删除、移动
Python 中使用 shutil 模块可实现文件或目录的复制、删除、移动;
2.1、文件和文件夹复制
a)、shutil.copyfileobj(fsrc , fdst , length)
复制类文件(file-like)对象 fsrc
的内容到类文件对象 fdst
。 可选整数参数 length
,指定缓冲区大小。若length
的值为负数,复制操作不会将源数据分块进行复制。 默认的,为了避免不可控制的内存消耗,数据会被分块存入chunk中;
注意: 如果 fsrc
对象的当前文件位置不为 0 ,则只有从当前文件位置到文件末位的内容会被复制
b)、shutil.copyfile(src, dst)
复制文件 src
的内容(不包含元素据)到文件 dst
中, dst
必须是一个完整的目标文件。
源文件src
和目标文件 dst
不能为同一个文件,否则会报错。
目标文件 dst
必须为可写状态,否则会触发 IOError。
如果 dst
已经存在,则会被覆盖。
注意:字符设备、块设备和管道不能使用此方法复制。
使用字符串指定src
和 dst
路径。
C)、shutil.copymode(src, dst)
复制 src
的文件权限位到 dst
。文件的内容、属主和用户组不会受影响。
使用字符串指定src
和 dst
路径。
d)、shutil.copystat(src , dst)
复制文件 src
的文件权限位、最后访问 access 时间、最后修改 modification 时间和**标识 flags **到 dst
。
文件的内容、属主和用户组不会受影响。
使用字符串指定src
和 dst
路径。
e)、shutil.copy(src, dst)
复制文件 src
到 dst
文件或文件夹中。
如果 dst
是文件夹, 则会在文件夹中创建或覆盖一个文件,且该文件与 src
的文件名相同。
文件权限位会被复制。
使用字符串指定src
和 dst
路径。
f)、shutil.copy2(src, dst)
shutil.copy2(src , dst)函数相比较shutil.copy()
增加了复制文件的元数据功能。
实际上,该方法是 shutil.copy()
和 shutil.copystat()
组合。
该方法相当于 Unix 命令的 ` cp -p `。
g)、shutil.ignore_patterns(*patterns)
该工厂函数创建了一个可以被调用的函数, 该函数可以用于 shutil.copytree()
的 ** ignore 参数**的值, 以跳过正确匹配的文件和文件夹
h)、shutil.copytree(src, dst, symlinks=False, ignore=None)
递归复制整个 src
文件夹。
目标文件夹 dst
不能已经存在;shutil.copytree()
方法会自动创建 dst
根文件夹。
文件夹权限和时间通过 shutil.copystat()
复制, 单独的文件通过 shutil.copy2()
复制。
如果“symlinks
”为真, 源文件夹中的符号链接将会被保留,但是原链接的元数据不会被复制。如果值为假或被省略,则链接文件指向文件的内容和元数据复制到新文件夹树中。
如果指定了 ignore
, 那么他必须是调用队列(callable),且作为 shutil.copytree()
的参数,参数包括文件夹本机及并通过 os.listdir()
返回文件夹包含的内容。
由于 shutil.copytree()
递归复制,因此 ignore
会在复制每个子文件夹的时候被调用。
callable必须返回一个由当前文件夹下的文件夹和文件所组成的队列(i.e. a subset of the items in the second argument); 这些文件夹和文件在复制过程中会被忽略。
可以使用 shutil.ignore_patterns()
创建callable。
如果发生意外, shutil.Error()
返回错误原因。
2.2、文件/文件夹的删除和移动
a)、shutil.rmtree(path , ignore_errors , onerror)
删除整个目录树;
path
必须指向一个文件夹,但不能是一个指向文件夹的符号链接;
如果 ` ignore_errors ` 值为真, 则删除失败时的信息将会被忽略。如果值为假或省略,那么这些错误将通过 onerror
指定的 handler 进行处理;
如果 onerror
被省略,则会 raise 一个异常。
如果指定了 onerror
,则必须包含三个参数: function, path 和 excinfo的 callable 。 第一个参数 ` function ` , 该函数用于 raise 异常;该函数可以是 os.path.islink(), os.listdir(), os.remove(), os.rmdir()
。 第二个参数 path
为传递给第一个参数的路径。 第三个参数 excinfo
为 sys.exc_info()
返回的异常信息;
通过 onerror
raise 的异常不会被捕捉;
b)、shutil.move(src, dst)
将一个文件或文件夹从 src
移动到 dst
。如果 dst
已存在且为文件夹,则 src
将会被移动到 dst
内。 如果如 dst
存在但不是一个文件夹, 取决于 os.rename()
的语义,dst
可能会被覆盖。 如果 dst
与 src
在相同的文件系统下, 则使用 os.rename()
。 否认将使用 shutil.copy2()
复制 src
到 dst
并删除;
c)、shutil.Error
该异常汇集文件操作时 raise 的异常。 例如 shutil.copytree()
, the exception argument is a list of 3-tuples (srcname, dstname, exception);
import os
# 输出当前工作目录
print("当前工作目录为:" , os.getcwd())
# 更改当前工作目录
os.chdir("/home/zx/ZX/MyProject/Python/Runoob/ZX_21_命名空间和作用域")
# 输出更新后工作目录
print("输出更新后工作目录:" , os.getcwd())
def mkfile(path):
# 判断文件是否存在,存在则删除,不存在创建
if os.path.exists(path):
os.removedirs(path)
print("文件删除成功")
else:
os.mkdir(path)
print("文件创建成功")
path = "/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/file.txt"
mkfile(path)
当前工作目录为: /home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库
输出更新后工作目录: /home/zx/ZX/MyProject/Python/Runoob/ZX_21_命名空间和作用域
文件删除成功
import os
# 创建一个文件dir(os),用于存储dir(os)输出内容
file_dir = os.open("./file_dir(os)" , os.O_RDWR | os.O_CREAT)
for i in dir(os):
os.write(file_dir , (i + '\n').encode("utf-8"))
os.close(file_dir)
import sys
import os
# 通过重定向,输出help(os)结果至文件
def WriteHelpFileByStdout(path ,mode , module):
# 通过重定向,设置系统输出结果至文件路径path
sys.stdout = open(path , mode)
# 系统输出help(module)结果
help(module)
# 关闭文件
sys.stdout.close()
path = './file_help(os)'
mode = 'w+'
module = os
WriteHelpFileByStdout(path ,mode , module)
三、文件通配符
Python的glob模块可以实现查找符合特定规则的文件路径名功能
查找文件一般用到如下三个匹配符:”” 、“?” 、“[]”
”” 匹配0个或多个字符;
”?” 匹配单个字符;
”[]” 匹配指定范围内的字符;如[0-9]匹配数字
3.1、glob.glob函数
以list的形式返回所有匹配的文件路径列表;
glob.glob函数只有pathname一个参数,定义了文件路径匹配规则,可以是绝对路径,也可以是相对路径;
3.2、glob.iglob函数
glob.iglob()函数获取一个可遍历对象,使用它可以逐个获取匹配的文件路径名。与glob.glob()的区别是:glob.glob()同时获取所有的匹配路径,而glob.iglob()一次只获取一个匹配路径;
"""
三、文件通配符
Python的glob模块可以实现查找符合特定规则的文件路径名功能
查找文件一般用到如下三个匹配符:”” 、“?” 、“[]”
”” 匹配0个或多个字符;
”?” 匹配单个字符;
”[]” 匹配指定范围内的字符;如[0-9]匹配数字
"""
import glob
import os
# glob.glob()函数一次性获取相对路径下指定格式的所有文件
Relative_path = r"./*.py"
print("glob.glob()函数一次性获取相对路径下指定格式的所有文件:")
for i in glob.glob(Relative_path):
print(i)
print("glob.glob(Relative_path)返回值类型:" , type(glob.glob(Relative_path)))
# glob.glob()函数一次性获取绝对路径下指定格式的所有文件
Absolute_path = os.getcwd() + "/*.py"
print("glob.glob()函数一次性获取绝对路径下指定格式的所有文件:")
for i in glob.glob(Absolute_path):
print(i)
# glob.iglob()函数一次获取相对路径下指定格式的一个文件
Relative_path = r"./*.py"
print("glob.iglob()函数一次获取相对路径下指定格式的一个文件:")
for i in glob.iglob(Relative_path):
print(i)
print("glob.iglob(Relative_path)返回值类型:" , type(glob.iglob(Relative_path)))
glob.glob()函数一次性获取相对路径下指定格式的所有文件:
./1.py
./ZX_01_操作系统.py
./__init__.py
./ZX_02_文件通配符glob函数.py
glob.glob(Relative_path)返回值类型: <class 'list'>
glob.glob()函数一次性获取绝对路径下指定格式的所有文件:
/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/1.py
/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/ZX_01_操作系统.py
/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/__init__.py
/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/ZX_02_文件通配符glob函数.py
glob.iglob()函数一次获取相对路径下指定格式的一个文件:
./1.py
./ZX_01_操作系统.py
./__init__.py
./ZX_02_文件通配符glob函数.py
glob.iglob(Relative_path)返回值类型: <class 'generator'>
3.3、使用glob.glob()获得文件路径
a)、获得当前文件夹下的所有文件
path参数为相对路径“./*”或者“*”;
b)、获得当前目录的某个子目录的文件A
子文件也属于相对路径,所以使用“./*/filenameA”或者“*/filenameA”。这里的“*/”代表下一层文件夹;
c)、当前处于目录A,获取目录B下文件C的路径
建议使用绝对路径,也就是使用类似“/*/*”的参数;
# “./*”或者“*”获得当前文件夹下的所有文件
print("(“./*”)或者(“*”)获得当前文件夹下的所有文件:"
, glob.glob("./*"))
# (“./*/filename”)或者(“*/filename”)获得当前目录的某个子目录的文件
print("(“./*/filename”)或者(“*/filename”)获得当前目录的某个子目录的文件:"
, glob.glob("./file/*"))
print("(“./*/filename”)或者(“*/filename”)获得当前目录的某个子目录的文件:"
, glob.glob("./*/file1.py"))
print("(“./*/filename”)或者(“*/filename”)获得当前目录的某个子目录的文件:"
, glob.glob("*/file2.py"))
# 当前处于目录A(ZX_22_标准库),获取目录B(ZX_21_命名空间和作用域)下文件C(file_dir(os))的路径
print(os.getcwd())
print(glob.glob("../*/file_dir(os)"))
(“./*”)或者(“*”)获得当前文件夹下的所有文件: ['./1.py', './ZX_01_操作系统.py', './file', './__init__.py', './ZX_02_文件通配符glob函数.py']
(“./*/filename”)或者(“*/filename”)获得当前目录的某个子目录的文件: ['./file/file2.py', './file/file1.py']
(“./*/filename”)或者(“*/filename”)获得当前目录的某个子目录的文件: ['./file/file1.py']
(“./*/filename”)或者(“*/filename”)获得当前目录的某个子目录的文件: ['file/file2.py']
/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库
['../ZX_21_命名空间和作用域/file_dir(os)']
四、命令行参数
sys.argv[]是一个从程序外部获取参数的桥梁,因我们从外部获取的参数可以是多个,所以sys.argv返回一个列表(list),所以才能用[]提取其中的元素,其中sys.argv[0]第一个元素是程序本身,即程序的路径,随后依次是外部给予的参数;
test.py代码如下:
# 四、命令行参数
# sys.argv[0]返回本文件路径
print("sys.argv[0]返回本文件路径:" , sys.argv[0])
# sys.argv返回列表list
print("sys.argv返回列表list:" , sys.argv)
# 从第二个元素开始输出
print("sys.argv[1:]:" , sys.argv[1:])
交互器中运行如下脚本,增加参数"this is a python"
输出结果如下:
zx@zx:~$ python3 '/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/1.py' this is a python
sys.argv[0]返回本文件路径: /home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/1.py
sys.argv返回列表list: ['/home/zx/ZX/MyProject/Python/Runoob/ZX_22_标准库/1.py', 'this', 'is', 'a', 'python']
sys.argv[1:]: ['this', 'is', 'a', 'python']
综上可看出,Sys.argv[ ]其实就是一个列表,里边的数据项就是用户输入的参数,参数是从程序外部输入的,而非代码本身,要想看到它的效果可以将程序保存,从外部运行程序并给出参数
五、错误输出重定向和程序终止
几种常见重定向的方式,参考文档:https://www.jb51.net/article/90506.htm
大多脚本的定向终止都使用 "sys.exit()"
5.1、控制台重定向
最简单常用的输出重定向方式是利用控制台命令,这种重定向由控制台完成,与Python本身无关;
Windows命令提示符(cmd.exe)和Linux Shell(bash等)均通过">"或">>"将输出重定向。其中,">"表示覆盖内容,">>"表示追加内容。类似地,"2>"可重定向标准错误。重定向到"nul"(Windows)或"/dev/null"(Linux)会抑制输出,既不屏显也不存盘;
以Windows命令提示符为例,将Python脚本输出重定向到文件:
E:\>echo print 'hello' > test.py # print 'hello' 代码块重定向写入test.py中
E:\>test.py > out.txt # test.py文件重定向至out.txt,覆盖内容
E:\>type out.txt # type命令直接显示文本文件内容,类似Linux系统的cat命令
hello
E:\>test.py >> out.txt # test.py文件重定向至out.txt,追加内容
E:\>type out.txt
hello
hello
E:\>test.py > nul # 重定向到"nul"(Windows)会抑制输出,既不屏显也不存盘
Linux Shell中执行Python脚本时,命令行应以"python"开头。除">"或">>"重定向外,还可使用tee命令。该命令可将内容同时输出到终端屏幕和(多个)文件中,"-a"选项表示追加写入,否则覆盖写入;
zx@zx:~$ python3 -c "print ('hello python')"
hello python
zx@zx:~$ python3 -c "print ('hello python')" > out.txt
zx@zx:~$ cat out.txt
hello python
zx@zx:~$ python3 -c "print ('hello python')" >> out.txt
zx@zx:~$ cat out.txt
hello python
hello python
zx@zx:~$ python3 -c "print ('I am')" | tee out.txt
I am
zx@zx:~$ python3 -c "print ('白杨')" | tee -a out.txt
白杨
zx@zx:~$ cat out.txt
I am
白杨
zx@zx:~$ python3 -c "print ('hello python')" > /dev/null
5.2、print 函数重定向
python2.x:
print >> filename , expr
此方式基于print语句的扩展形式,即"print obj >> expr
"(输出的obj对象追加重定向至expr中);
其中obj
为一个file-like(尤其是提供write方法的)对象,为None时对应标准输出(sys.stdout);expr
将被输出到该文件对象中;
python3.x:
print( expr , file = filename )
# print 函数重定向
from io import StringIO
import sys
memo=StringIO()
serr=sys.stderr
file=open('out.txt','w+')
print('StringIO' , memo)
print('stderr' , serr)
print('file' , file)
print(memo.getvalue() , None)
StringIO <_io.StringIO object at 0x7f08dba15168>
stderr <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
file <_io.TextIOWrapper name='out.txt' mode='w+' encoding='UTF-8'>
None
5.3、sys.stdout重定向
将一个可写对象(如file-like对象)赋给sys.stdout,可使随后的print语句输出内容重定向至该对象。重定向结束后,将sys.stdout恢复最初的缺省值,即标准输出。
import sys
# 5.3、sys.stdout重定向
# 方法一:
# 保存标准输出流
savedStdout = sys.stdout
# 打开文件,用于保存标准输出流内容
with open('./out.txt', 'w+') as file:
# 标准输出重定向至文件,以下print输出的内容均被重定向至文件file内
sys.stdout = file
print('方法:This message is for file!')
print('方法:print输出的内容被重定向至文件file内')
# 恢复标准输出流
sys.stdout = savedStdout
print('方法:This message is for screen!')
print('方法:print输出的内容显示在屏幕上')
方法:This message is for screen!
方法:print输出的内容显示在屏幕上
import os, sys
from io import StringIO
# 自定义多种具有write()方法的file-like对象,满足不同重定向需求
class RedirectStdout:
def __init__(self):
self.content = ''
self.savedStdout = sys.stdout
self.memObj, self.fileObj, self.nulObj = None, None, None
# 外部的print语句将执行本write()方法,并由当前sys.stdout输出
def write(self, outStr):
self.content.append(outStr)
#self.content += outStr
# 标准输出重定向至控制台
def toCons(self):
# sys.__stdout__
sys.stdout = self.savedStdout
# 标准输出重定向至内存
def toMemo(self):
self.memObj = StringIO()
sys.stdout = self.memObj
# 标准输出重定向至文件
def toFile(self, file='./out.txt'):
self.fileObj = open(file, 'a+', 1) # 改为行缓冲
sys.stdout = self.fileObj
# 抑制输出
def toMute(self):
self.nulObj = open(os.devnull, 'w')
sys.stdout = self.nulObj
def restore(self):
self.content = ''
if self.memObj.closed != True:
self.memObj.close()
if self.fileObj.closed != True:
self.fileObj.close()
if self.nulObj.closed != True:
self.nulObj.close()
sys.stdout = self.savedStdout # sys.__stdout__
# 实例化
redirObj = RedirectStdout()
#本句会抑制"Let's begin!"输出
sys.stdout = redirObj
print("Let's begin!")
# 屏显'Hello World!'和'I am Gavin.'(两行)
print('Hello World!' , redirObj.toCons())
print('I am Gavin' , redirObj.toCons())
# 写入'How are you?'和"Can't complain."(两行)
print('How are you?' , redirObj.toFile())
print("Can't complain." , redirObj.toFile())
# 屏显
print("What'up?" , redirObj.toCons())
# 无屏显或写入
print('<Silence>' , redirObj.toMute())
# 控制台屏显'Never redirect me!'
os.system('echo Never redirect me!')
# 无屏显或写入
print('What a pity!' , redirObj.toMemo())
# 屏显
print('Hello?' , redirObj.toCons())
# 该串写入文件
print("Oh, Gavin can't hear me" , redirObj.toFile())
redirObj.restore()
# 屏显
print('Pop up')
Hello World! None
I am Gavin None
What'up? None
Never redirect me!
Hello? None
Pop up
out.txt文件内容:
How are you? None
Can't complain. None
Oh, Gavin can't hear me None
5.4、上下文管理器(Context Manager)
借助于上下文管理器语法,可不必向重定向使用者暴露sys.stdout
首先考虑输出抑制,基于上下文管理器语法实现如下:
import sys , contextlib
from io import StringIO
class DummyFile:
def write(self, outStr): pass
@contextlib.contextmanager
def MuteStdout():
savedStdout = sys.stdout
sys.stdout = StringIO() #DummyFile()
try:
yield
except Exception: #捕获到错误时,屏显被抑制的输出(该处理并非必需)
content, sys.stdout = sys.stdout, savedStdout
print (content.getvalue()) #; raise
#finally:
sys.stdout = savedStdout
使用示例如下:
with MuteStdout():
print "I'll show up when <raise> is executed!" #不屏显不写入
raise #屏显上句
print "I'm hiding myself somewhere:)" #不屏显
再考虑更通用的输出重定向:
import os, sys
from contextlib import contextmanager
@contextmanager
def RedirectStdout(newStdout):
savedStdout, sys.stdout = sys.stdout, newStdout
try:
yield
finally:
sys.stdout = savedStdout
使用示例如下:
def Greeting(): print 'Hello, boss!'
with open('out.txt', "w+") as file:
print "I'm writing to you..." #屏显
with RedirectStdout(file):
print 'I hope this letter finds you well!' #写入文件
print 'Check your mailbox.' #屏显
with open(os.devnull, "w+") as file, RedirectStdout(file):
Greeting() #不屏显不写入
print 'I deserve a pay raise:)' #不屏显不写入
print 'Did you hear what I said?' #屏显
可见,with内嵌块里的函数和print语句输出均被重定向。注意,上述示例不是线程安全的,主要适用于单线程。
当函数被频繁调用时,建议使用装饰器包装该函数。这样,仅需修改该函数定义,而无需在每次调用该函数时使用with语句包裹。示例如下:
import sys, cStringIO, functools
def MuteStdout(retCache=False):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
savedStdout = sys.stdout
sys.stdout = cStringIO.StringIO()
try:
ret = func(*args, **kwargs)
if retCache == True:
ret = sys.stdout.getvalue().strip()
finally:
sys.stdout = savedStdout
return ret
return wrapper
return decorator
若装饰器MuteStdout的参数retCache为真,外部调用func()
函数时将返回该函数内部print输出的内容(可供屏显);若retCache为假,外部调用func()
函数时将返回该函数的返回值(抑制输出)。
MuteStdout装饰器使用示例如下:
@MuteStdout(True)
def Exclaim(): print 'I am proud of myself!'
@MuteStdout()
def Mumble(): print