各位观众,大家好!我是今天的主讲人,江湖人称“代码老司机”。今天咱们聊聊 Python 里一个低调但实力超群的模块:shutil
。这玩意儿就像文件操作界的瑞士军刀,能复制、移动、重命名,还能打包压缩,绝对是居家旅行、办公必备良品。
一、初识 shutil
:你的文件操作小助手
简单来说,shutil
模块是 Python 标准库中的一员,专门用来处理高级的文件和目录操作。它建立在 os
模块的基础上,提供了更强大、更便捷的功能。如果你觉得 os.path
里的那些函数不够用,或者写起来太繁琐,那就该考虑 shutil
了。
二、shutil
的常用功能:十八般武艺样样精通
接下来,咱们逐一过过 shutil
的常用功能,看看它到底有多厉害。
1. 文件复制:克隆大法好!
-
shutil.copy(src, dst)
:简单复制这是最基本的复制操作,将文件
src
复制到dst
。如果dst
是一个目录,则会在该目录下创建一个同名文件。import shutil src_file = 'my_file.txt' dst_file = 'my_file_copy.txt' # 先创建一个源文件 with open(src_file, 'w') as f: f.write('Hello, shutil!') shutil.copy(src_file, dst_file) print(f"文件 '{src_file}' 已复制到 '{dst_file}'")
-
shutil.copy2(src, dst)
:复制文件和元数据copy2
不仅复制文件内容,还会尽可能地保留原始文件的元数据,比如修改时间、访问时间等。这对于需要精确保留文件信息的场景非常有用。import shutil import os import time src_file = 'my_file.txt' dst_file = 'my_file_copy2.txt' # 先修改源文件的修改时间 modification_time = time.time() - 3600 # 一小时前 os.utime(src_file, (modification_time, modification_time)) shutil.copy2(src_file, dst_file) # 验证修改时间是否被保留 src_mtime = os.path.getmtime(src_file) dst_mtime = os.path.getmtime(dst_file) print(f"源文件修改时间:{src_mtime}") print(f"复制文件修改时间:{dst_mtime}") assert abs(src_mtime - dst_mtime) < 1, "修改时间未被正确复制" # 允许1秒的误差
-
shutil.copyfile(src, dst)
:仅复制文件内容copyfile
只复制文件的内容,不关心任何元数据。注意,dst
必须是一个文件路径,不能是目录。如果dst
文件已存在,会被覆盖。import shutil src_file = 'my_file.txt' dst_file = 'my_file_copyfile.txt' shutil.copyfile(src_file, dst_file) print(f"文件 '{src_file}' 的内容已复制到 '{dst_file}'")
2. 目录复制:一键克隆整个文件夹
-
shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False)
:复制整个目录树这绝对是
shutil
的王牌功能之一。它可以递归地复制整个目录树,包括所有文件和子目录。symlinks
: 如果为True
,复制符号链接本身,而不是链接指向的文件。默认为False
,复制链接指向的文件。ignore
: 一个可调用对象,用于过滤不需要复制的文件或目录。copy_function
: 用于复制文件的函数,默认为copy2
。ignore_dangling_symlinks
: 如果为True
,忽略无效的符号链接。dirs_exist_ok
: 如果为True
,且目标目录已存在,不会抛出异常。
import shutil import os src_dir = 'my_source_directory' dst_dir = 'my_destination_directory' # 先创建一个源目录和一些文件 os.makedirs(src_dir, exist_ok=True) with open(os.path.join(src_dir, 'file1.txt'), 'w') as f: f.write('This is file1.') os.makedirs(os.path.join(src_dir, 'subdir'), exist_ok=True) with open(os.path.join(src_dir, 'subdir', 'file2.txt'), 'w') as f: f.write('This is file2.') shutil.copytree(src_dir, dst_dir) print(f"目录 '{src_dir}' 已复制到 '{dst_dir}'")
ignore
参数的用法:import shutil import os def ignore_patterns(*patterns): def _ignore_patterns(path, names): from fnmatch import filter ignored_names = set() for pattern in patterns: ignored_names.update(filter(names, pattern)) return set(os.path.join(path, name) for name in ignored_names) return _ignore_patterns src_dir = 'my_source_directory' dst_dir = 'my_destination_directory' # 忽略所有 .txt 文件 shutil.copytree(src_dir, dst_dir, ignore=ignore_patterns('*.txt')) print(f"目录 '{src_dir}' 已复制到 '{dst_dir}',忽略了所有 .txt 文件")
3. 文件和目录移动:乾坤大挪移!
-
shutil.move(src, dst, copy_function=copy2)
:移动文件或目录move
函数可以将文件或目录从src
移动到dst
。如果dst
是一个目录,则src
会被移动到该目录下。如果dst
文件已存在,则会被覆盖。如果源和目标在同一文件系统上,move
会简单地重命名src
。否则,src
将被复制到dst
,然后src
被删除。import shutil import os src_file = 'my_file.txt' dst_file = 'moved_file.txt' # 先创建一个源文件 with open(src_file, 'w') as f: f.write('Hello, move!') shutil.move(src_file, dst_file) print(f"文件 '{src_file}' 已移动到 '{dst_file}'")
移动目录的例子:
import shutil import os src_dir = 'my_source_directory' dst_dir = 'moved_directory' # 先创建一个源目录 os.makedirs(src_dir, exist_ok=True) shutil.move(src_dir, dst_dir) print(f"目录 '{src_dir}' 已移动到 '{dst_dir}'")
4. 文件和目录删除:斩草要除根!
-
shutil.rmtree(path, ignore_errors=False, onerror=None)
:递归删除整个目录树这个函数非常强大,但也非常危险!它可以递归地删除整个目录树,包括所有文件和子目录。使用时务必小心,确保你真的想删除这些东西。
ignore_errors
: 如果为True
,忽略删除过程中出现的错误。onerror
: 一个可调用对象,用于处理删除过程中出现的错误。
import shutil import os target_dir = 'my_target_directory' # 先创建一个目标目录和一些文件 os.makedirs(target_dir, exist_ok=True) with open(os.path.join(target_dir, 'file1.txt'), 'w') as f: f.write('This is file1.') shutil.rmtree(target_dir) print(f"目录 '{target_dir}' 已被删除")
onerror
参数的用法:import shutil import os def handle_error(function, path, excinfo): print(f"删除 '{path}' 时发生错误:{excinfo}") target_dir = 'my_target_directory' # 先创建一个目标目录和一些文件 os.makedirs(target_dir, exist_ok=True) with open(os.path.join(target_dir, 'file1.txt'), 'w') as f: f.write('This is file1.') # 创建一个只读文件,模拟删除错误 os.chmod(os.path.join(target_dir, 'file1.txt'), 0o444) # 设置为只读 shutil.rmtree(target_dir, onerror=handle_error) print(f"目录 '{target_dir}' 尝试被删除(可能部分删除)")
5. 文件归档和解包:打包带走!
-
shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0, owner=None, group=None, logger=None)
:创建归档文件这个函数可以将一个目录或一组文件打包成归档文件,支持多种格式,比如 zip、tar、gztar、bztar、xztar。
base_name
: 归档文件的基本名称,不包括扩展名。format
: 归档格式,比如 "zip", "tar", "gztar", "bztar", "xztar"。root_dir
: 归档的根目录。base_dir
: 归档的起始目录,相对于root_dir
。
import shutil import os source_dir = 'my_source_directory' archive_name = 'my_archive' # 先创建一个源目录和一些文件 os.makedirs(source_dir, exist_ok=True) with open(os.path.join(source_dir, 'file1.txt'), 'w') as f: f.write('This is file1.') os.makedirs(os.path.join(source_dir, 'subdir'), exist_ok=True) with open(os.path.join(source_dir, 'subdir', 'file2.txt'), 'w') as f: f.write('This is file2.') archive_path = shutil.make_archive(archive_name, 'zip', root_dir='.', base_dir=source_dir) print(f"目录 '{source_dir}' 已归档到 '{archive_path}'")
-
shutil.unpack_archive(filename, extract_dir=None, format=None)
:解包归档文件这个函数可以将一个归档文件解包到指定的目录。
filename
: 归档文件的路径。extract_dir
: 解包的目标目录,默认为当前目录。format
: 归档格式,如果为None
,则自动检测。
import shutil import os archive_file = 'my_archive.zip' extract_dir = 'my_extracted_directory' shutil.unpack_archive(archive_file, extract_dir) print(f"文件 '{archive_file}' 已解包到 '{extract_dir}'")
-
shutil.get_archive_formats()
:获取支持的归档格式这个函数可以返回一个列表,包含所有支持的归档格式。
import shutil formats = shutil.get_archive_formats() print("支持的归档格式:") for format_name, (description, extensions, read, write) in formats: print(f" {format_name}: {description}, extensions: {extensions}")
6. 磁盘空间:你的硬盘还好吗?
-
shutil.disk_usage(path)
:获取磁盘使用情况这个函数可以返回指定路径的磁盘使用情况,包括总空间、已用空间和可用空间。
import shutil total, used, free = shutil.disk_usage('.') print(f"总空间: {total // (2**30)} GB") print(f"已用空间: {used // (2**30)} GB") print(f"可用空间: {free // (2**30)} GB")
7. 终端大小:你的窗口有多大?
-
shutil.get_terminal_size(fallback=(columns, lines))
:获取终端大小这个函数可以返回当前终端的列数和行数。如果无法获取终端大小,则返回
fallback
参数指定的值。import shutil columns, lines = shutil.get_terminal_size() print(f"终端列数: {columns}") print(f"终端行数: {lines}")
三、shutil
的异常处理:稳如老狗!
在使用 shutil
时,可能会遇到各种各样的异常,比如文件不存在、权限不足等。为了保证程序的健壮性,我们需要进行适当的异常处理。
常见的异常:
shutil.Error
: 这是shutil
模块中所有异常的基类。OSError
: 操作系统相关的错误,比如文件不存在、权限不足等。IOError
: 输入输出错误。
下面是一个简单的异常处理示例:
import shutil
import os
src_file = 'non_existent_file.txt'
dst_file = 'copy_of_non_existent_file.txt'
try:
shutil.copy2(src_file, dst_file)
except FileNotFoundError:
print(f"源文件 '{src_file}' 不存在!")
except PermissionError:
print(f"没有权限复制文件!")
except shutil.Error as e:
print(f"复制文件时发生错误:{e}")
except Exception as e:
print(f"发生未知错误:{e}")
四、总结:shutil
,文件操作的得力助手
shutil
模块提供了丰富的文件和目录操作功能,可以大大简化我们的代码,提高开发效率。无论是简单的文件复制,还是复杂的目录归档,shutil
都能胜任。掌握 shutil
,你就能轻松驾驭各种文件操作场景,成为真正的文件操作大师!
五、一些使用 shutil
的小贴士:
- 在使用
rmtree
删除目录时,务必小心,确认你要删除的目录是正确的。 - 在使用
copytree
复制目录时,可以利用ignore
参数过滤不需要复制的文件或目录。 - 在处理大量文件时,可以考虑使用多线程或多进程来提高效率。
- 熟悉各种归档格式的特点,选择合适的格式进行归档。
最后,希望今天的分享对大家有所帮助。记住,代码的世界是充满乐趣的,只要你肯学习、肯实践,就能掌握各种强大的工具,创造出令人惊叹的作品! 感谢各位的观看,下次再见!