
文章目录
shutil 模块提供了一系列对文件和文件集合的高阶操作,特别是提供了一些支持文件拷贝和删除的函数,主要功能有对文件、文件夹的 复制、删除、移动操作,以及压缩包的处理模块
本文章内容较多,如果需要查找某个特定的方法或属性,建议使用浏览器的 查找
ctrl + f功能
在看下列内容并实际操作前,别忘记导入shutil 标准库
import shutil
1)、shutil.copyfileobj(fsrc, fdst[, length]):
将文件类对象 fsrc 的内容拷贝到文件类对象 fdst 中,整数值 length 如果给出则设为缓冲区大小,特别的,length为负值事表示拷贝数据时不对源数据进行分块循环处理;默认情况下会分块读取数据以避免不受控制的内存消耗。
演示代码:👇
# 复制文件A中的全部内容到文件B
with open(file_A, "r", encoding="utf-8") as f_r, open(file_B, "w", encoding="utf-8") as f_w:
shutil.copyfileobj(f_r, f_w)
如果 fsrc 对象的当前指针位置不为0,则会当前文件指针位置到文件末尾的内容会被拷贝
with open(file_A, "r", encoding="utf-8") as f_r, open(file_B, "w", encoding="utf-8") as f_w:
# 将文件A中光标指向3,即第二个字符为开头
# 注意对于UTF-8编码,每三间隔为一个字符,不为三的倍数将会抛出异常
f_r.seek(3)
shutil.copyfileobj(f_r, f_w)
2)、shutil.copyfile(src, dst, *, follow_symlinks=True):「常用」
将名为 src 的文件的内容(不包括元数据)拷贝到名为 dst 的文件并以尽可能高效的方式返回 dst(即目标文件路径)
参数如下:
如果 src 和 dst 指定了同一个文件,则将引发 SameFileError
目标位置必须是可写的,否则将引发 OSError 异常
演示代码:👇
不同于shutil.copyfileobj,此方法无需目标文件存在,会自动创建生成一个新的文件,如果目标文件已经存在,它将被替换
shutil.copyfile(file_A, file_B)
3)、shutil.copymode(src, dst, *, follow_symlinks=True):
从 src 拷贝权限位到 dst,文件的内容、所有者和分组将不受影响
参数如下:
演示代码:👇
shutil.copymode(file_A, file_B)
4)、shutil.copystat(src, dst, *, follow_symlinks=True):
从 src 拷贝状态信息,文件的内容、所有者和分组将不受影响,如:权限位(mode)、最近访问时间(atime)、最近修改时间(mtime)以及旗标(flags)到 dst。 在 Linux上,copystat() 还会在可能的情况下拷贝“扩展属性”。
参数如下:
演示代码:👇
shutil.copystat(file_A, file_B)
5)、shutil.copy(src, dst, *, follow_symlinks=True):「常用」
将文件 src 内容和权限 拷贝到文件或目录 dst,并返回目标文件所对应的路径。
参数如下:
源码:
源码其实很简单,其实就是同时调用了shutil.copyfile 和copymode,可以把shutil.copy 理解成这两个shutil.copyfile 和copymode的结合
def copy(src, dst, *, follow_symlinks=True):
"""Copy data and mode bits ("cp src dst"). Return the file's destination.
The destination may be a directory.
If follow_symlinks is false, symlinks won't be followed. This
resembles GNU's "cp -P src dst".
If source and destination are the same file, a SameFileError will be
raised.
"""
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
演示代码:👇
shutil.copy(file_A, file_B)
6)、shutil.copy2(src, dst, *, follow_symlinks=True):「常用」
类似于 shutil.copy,区别在于 shutil.copy2 在拷贝文件内容时还会拷贝文件状态的信息
参数如下:
源码:
和shutil.copy是一个套路,就是同时调用了shutil.copyfile 和shutil.copystat
def copy2(src, dst, *, follow_symlinks=True):
"""Copy data and metadata. Return the file's destination.
Metadata is copied with copystat(). Please see the copystat function
for more information.
The destination may be a directory.
If follow_symlinks is false, symlinks won't be followed. This
resembles GNU's "cp -P src dst".
"""
if os.path.isdir(dst):
dst = os.path.join(dst, os.path.basename(src))
copyfile(src, dst, follow_symlinks=follow_symlinks)
copystat(src, dst, follow_symlinks=follow_symlinks)
return dst
演示代码:👇
shutil.copy2(file_A, file_B)
1)、shutil.ignore_patterns(*patterns):
这个函数为shutil.copytree的辅助函数,作用于 shutil.copytree 的 ignore 可调用对象参数,以 glob 风格忽略 shutil.copytree 中的文件和目录,具体使用请移步到 2)、shutil.copytree
glob介绍:
glob 类似于正则表达式,但与正则表达式又不完全相同,可以简单的将其理解成一个简化的正则,glob非常强大的用途在于路径匹配,另外每个平台和开发语言所支持glob路径匹配可能不完全一样
.abcd[abcd]可以匹配abcd中的任意一个字符[^abcd]可以匹配除了abcd之外的任意一个字符!test.py 可以排除掉 test.py 文件演示代码:👇
# 比较常用的是忽略掉以.pyc结尾以及tmp开头的文件
shutil.ignore_patterns('*.pyc', 'tmp*')
# 忽略掉以.log和.tmp结尾的文件
shutil.ignore_patterns('*.log', '*.tmp')
2)、shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False):「常用」
将src整个目录树拷贝到dst目录,会自动创建dst目录文件夹并返回目标目录的路径,目录的权限和时间会通过 shutil.copystat 来拷贝,单个文件则会使用 shutil.copy2 来拷贝
参数如下:
shutil.ignore_patterns 创建这种基于 glob 风格模式来忽略特定名称的可调用对象copy_function 参数以允许提供定制的拷贝函数,必须是一个将被用来拷贝每个文件的可调用对象,默认情况下使用shutil.copy2,即拷贝文件内容时还会拷贝文件状态的信息目标目录不能存在,否则会抛出FileExistsError异常
演示代码:👇
比较常用的调用方法
# 将文件夹A拷贝到文件夹B
shutil.copytree(folder_A, folder_B)
# 将文件夹A拷贝到文件夹B, 在此过程中忽略掉以.log和.tmp结尾的文件
shutil.copytree(folder_A, folder_B, ignore=shutil.ignore_patterns('*.log', '*.tmp'))
再结合copy_function参数,将原来的 shutil.copy2(拷贝内容和文件状态的信息) 更改为 shutil.copy(拷贝内容和权限)
# 将文件夹A拷贝到文件夹B, 拷贝文件的内容和权限
shutil.copytree(folder_A, folder_B, copy_function=shutil.copy)
在上诉的参数说明中提到过,copy_function 参数以允许提供定制的拷贝函数,没错我们可以使用自己自定义的函数对每个文件进行拷贝操作,如下方代码所示,结合了 shutil.copyfile 、shutil.copystat、shutil.copymode 对文件进行拷贝
def copy_mode(*args, **kwargs):
shutil.copyfile(*args, **kwargs)
shutil.copystat(*args, **kwargs)
shutil.copymode(*args, **kwargs)
# 将文件夹A拷贝到文件夹B, 并以自己自定义的拷贝文件方式拷贝
shutil.copytree(folder_A, folder_B, copy_function=copy_mode)
注意在使用
copy_function参数时,应传入函数本身而不是加上括号调用函数
3)、shutil.rmtree(path, ignore_errors=False, οnerrοr=None):「常用」
删除一个完整的目录树,此方法会经常用到
需要注意的是,文件夹分两种,一种是空文件夹,另外一种则是文件夹中有内容的非空文件夹,对于空文件夹来说,可以使用os标准模块下的os.rmdir将其删除,但如果对非空文件夹删除会出现 OSError 错误,对于删除非空文件夹,一种方法是使用os模块下的os.walk将文件与文件夹分别遍历删除,另外一种方法就是使用现所讲的shutil.rmtree。
详情请移步到我所写的文章 《Python 以优雅的姿势 操作文件》 下的删除文件夹
参数如下:
True,删除失败导致的错误将被忽略;默认为False,此类错误将通过调用由 onerror 所指定的处理程序来处理,或者如果此参数不传入则将引发一个异常。演示代码:👇
可以配合os标准库模块下的os.path.isdir判断是否为文件夹再进行删除
# 判断路径是否为文件夹
if os.path.isdir(path):
# 删除一个完整的目录树
shutil.rmtree(path)
利用 ignore_errors 以及 onerror 参数,将遇到删除失败时的异常变成自定义的报错
def myerr(*args, **kwargs):
raise Exception("删除文件夹时发生错误")
shutil.rmtree("不存在的路径", ignore_errors=False, onerror=myerr)
4)、shutil.move(src, dst, copy_function=copy2):「常用」
递归地将一个文件或目录 (src) 移至另一位置 (dst) 并返回目标位置路径
参数如下:
copy_function 参数以允许提供定制的拷贝函数,默认为shutil.copy2,这与 shutil.copytree 中的copy_function参数类似源码:
来看看源码,其实这个移动原理很简单,其实就是先将文件或文件夹拷贝到目标目录,再将原目录进行删除操作,注意看倒数第五、第六行
def move(src, dst, copy_function=copy2):
"""Recursively move a file or directory to another location. This is
similar to the Unix "mv" command. Return the file or directory's
destination.
If the destination is a directory or a symlink to a directory, the source
is moved inside the directory. The destination path must not already
exist.
If the destination already exists but is not a directory, it may be
overwritten depending on os.rename() semantics.
If the destination is on our current filesystem, then rename() is used.
Otherwise, src is copied to the destination and then removed. Symlinks are
recreated under the new name if os.rename() fails because of cross
filesystem renames.
The optional `copy_function` argument is a callable that will be used
to copy the source or it will be delegated to `copytree`.
By default, copy2() is used, but any function that supports the same
signature (like copy()) can be used.
A lot more could be done here... A look at a mv.c shows a lot of
the issues this implementation glosses over.
"""
sys.audit("shutil.move", src, dst)
real_dst = dst
if os.path.isdir(dst):
if _samefile(src, dst):
# We might be on a case insensitive filesystem,
# perform the rename anyway.
os.rename(src, dst)
return
real_dst = os.path.join(dst, _basename(src))
if os.path.exists(real_dst):
raise Error("Destination path '%s' already exists" % real_dst)
try:
os.rename(src, real_dst)
except OSError:
if os.path.islink(src):
linkto = os.readlink(src)
os.symlink(linkto, real_dst)
os.unlink(src)
elif os.path.isdir(src):
if _destinsrc(src, dst):
raise Error("Cannot move a directory '%s' into itself"
" '%s'." % (src, dst))
if (_is_immutable(src)
or (not os.access(src, os.W_OK) and os.listdir(src)
and sys.platform == 'darwin')):
raise PermissionError("Cannot move the non-empty directory "
"'%s': Lacking write permission to '%s'."
% (src, src))
copytree(src, real_dst, copy_function=copy_function,
symlinks=True)
rmtree(src)
else:
copy_function(src, real_dst)
os.unlink(src)
return real_dst
演示代码:👇
# 将文件夹A移动到文件夹B目标路径
shutil.move(folder_A, folder_B)
5)、shutil.disk_usage(path)
path 可以是一个文件或是一个目录,返回一个具名元组表示给定路径的磁盘使用统计数据,其中包含 total, used 和 free 属性,分别表示总计、已使用和未使用空间的字节数
演示代码:👇
print(shutil.disk_usage(folder_A))
usage(total=1000186310656, used=874484068352, free=125702242304)
shutil标准库模块提供了用于 创建和读取压缩 和 归档文件 的高层级工具,但其实这些功能依赖于 zipfile 和 tarfile 模块,换句话来说就等同于在使用这两个标准库模块
1)、shutil.make_archive(base_name, format[, root_dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]])
创建一个归档文件「压缩包」,例如 zip 或 tar,并返回其归档(压缩包)的文件路径
参数如下:
test.zip将会再添加一次扩展名,输出的压缩包文件名为test.zip.zipbase_dir 必须相对于 root_dir 给出,简单来说就是对root_dir进一步进行划分,要从root_dir的哪一个位置下进行压缩,默认为当前目录即root_dir目录True,则不会创建归档文件,但将要被执行的操作会被记录到 logger演示代码:👇
基本用法
zip_path = shutil.make_archive(
# 压缩包文件名, 无后缀
"test1",
# 使用zip格式压缩包
"zip",
# 被压缩的路径
root_dir="./test/1.txt",
)
配合 base_dir 参数使用
zip_path = shutil.make_archive(
# 压缩包文件名, 无后缀
"test2",
# 使用zip格式压缩包
"zip",
# 被压缩的路径
root_dir="./test",
# 在root_dir的基础上,其路径下的文件或文件夹
base_dir="文件夹一"
)
2)、shutil.unpack_archive(filename[, extract_dir[, format]])
解包一个归档文件「压缩包」
参数如下:
shutil.register_unpack_format注册的其他格式,如未提供将使用压缩包的扩展名来检查是否注册了对应于该扩展名的解包器如果文件名或文件夹名为中文,会出现乱码情况,估计内部使用的是ASCII编码,建议目录均以英文数字命名

在未找到任何解包器的情况下,将引发 ValueError
演示代码:👇
shutil.unpack_archive(
"test.zip",
"./目标文件夹",
format="zip"
)
3)、shutil.register_archive_format(name, function[, extra_args[, description]])
为 name 格式注册一个归档器
4)、shutil.register_unpack_format(name, extensions, function[, extra_args[, description]])
注册一个解包格式
5)、shutil.unregister_archive_format(name)
从支持的格式中移除归档格式
name为格式的名称
6)、shutil.unregister_unpack_format(name)
撤销注册的一个解包格式
name 为格式的名称
7)、shutil.get_archive_formats()
返回支持的归档格式列表
所返回序列中的每个元素为一个元组(名字,描述)「name, description」
默认情况下 shutil 提供以下格式:
演示代码:👇
print(shutil.get_archive_formats())
[('bztar', "bzip2'ed tar-file"), ('gztar', "gzip'ed tar-file"), ('tar', 'uncompressed tar file'), ('xztar', "xz'ed tar-file"), ('zip', 'ZIP file')]
8)、shutil.get_unpack_formats()
返回所有已注册的解包格式列表。
所返回序列中的每个元素为一个元组(名字,扩展,描述)「name, extensions, description」
默认情况下 shutil 提供以下格式:
演示代码:👇
print(shutil.get_unpack_formats())
[('bztar', ['.tar.bz2', '.tbz2'], "bzip2'ed tar-file"), ('gztar', ['.tar.gz', '.tgz'], "gzip'ed tar-file"), ('tar', ['.tar'], 'uncompressed tar file'), ('xztar', ['.tar.xz', '.txz'], "xz'ed tar-file"), ('zip', ['.zip'], 'ZIP file')]
1)、shutil.get_terminal_size(fallback=(columns, lines)):
获取终端窗口的尺寸
会分别检查环境变量 columns 和 lines 两个维度。如果定义了这些变量并且其值为正整数,则将使用这些值。默认情况下未定义 columns 或 lines,则连接到 sys.__stdout__ 的终端将通过发起调用os.get_terminal_size() 被查询。
如果由于系统不支持查询,或是由于我们未连接到某个终端而导致查询终端尺寸不成功,则会使用在 fallback 形参中给出的值,fallback 默认为 (80, 24),这是许多终端模拟器所使用的默认尺寸。
源码:
因为本函数是涉及到os标准库模块中的功能,且实现代码比较简单,我认为还是有必要将源码放出来看看的
def get_terminal_size(fallback=(80, 24)):
"""Get the size of the terminal window.
For each of the two dimensions, the environment variable, COLUMNS
and LINES respectively, is checked. If the variable is defined and
the value is a positive integer, it is used.
When COLUMNS or LINES is not defined, which is the common case,
the terminal connected to sys.__stdout__ is queried
by invoking os.get_terminal_size.
If the terminal size cannot be successfully queried, either because
the system doesn't support querying, or because we are not
connected to a terminal, the value given in fallback parameter
is used. Fallback defaults to (80, 24) which is the default
size used by many terminal emulators.
The value returned is a named tuple of type os.terminal_size.
"""
# columns, lines are the working values
try:
columns = int(os.environ['COLUMNS'])
except (KeyError, ValueError):
columns = 0
try:
lines = int(os.environ['LINES'])
except (KeyError, ValueError):
lines = 0
# only query if necessary
if columns <= 0 or lines <= 0:
try:
size = os.get_terminal_size(sys.__stdout__.fileno())
except (AttributeError, ValueError, OSError):
# stdout is None, closed, detached, or not a terminal, or
# os.get_terminal_size() is unsupported
size = os.terminal_size(fallback)
if columns <= 0:
columns = size.columns
if lines <= 0:
lines = size.lines
return os.terminal_size((columns, lines))
演示代码:👇
返回的值是一个 os.terminal_size 类型的具名元组。
print(shutil.get_terminal_size())
os.terminal_size(columns=80, lines=24)
待更新,敬请期待
在这里插入代码片
由衷感谢💖
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信
我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A