Python os.stat 函数
上次修改时间:2025 年 4 月 11 日
本篇综合指南探讨了 Python 的 os.stat 函数,该函数用于检索详细的文件系统元数据。我们将介绍 stat 结构属性、平台差异以及实际的文件信息示例。
基本定义
os.stat 函数返回一个 stat_result 对象,其中包含文件系统元数据。 它适用于所有平台上的文件和目录。
关键属性包括 st_mode(权限)、st_size(大小)、st_mtime(修改时间)。 可用的确切属性因操作系统而异。
获取基本文件信息
os.stat 最简单的用法是检索基本文件元数据,如大小和修改时间。 此示例显示了常见的可用属性。
import os
import time
file_path = "example.txt"
stats = os.stat(file_path)
print(f"File: {file_path}")
print(f"Size: {stats.st_size} bytes")
print(f"Last modified: {time.ctime(stats.st_mtime)}")
print(f"Last accessed: {time.ctime(stats.st_atime)}")
print(f"Created: {time.ctime(stats.st_ctime)}")
print(f"Mode: {oct(stats.st_mode)}")
print(f"Inode: {stats.st_ino}")
此代码检索并显示常见的文件属性。 请注意,st_ctime 在 Windows 上表示创建时间,但在类 Unix 系统上表示元数据更改时间。
st_mode 属性以八进制格式显示文件权限和类型。 我们将在后面的示例中更详细地探讨这一点。
检查文件类型
st_mode 属性包含文件类型信息,可以使用 stat 模块常量进行检查。 这有助于区分文件和目录。
import os
import stat
path = "example.txt"
stats = os.stat(path)
mode = stats.st_mode
if stat.S_ISREG(mode):
print(f"{path} is a regular file")
elif stat.S_ISDIR(mode):
print(f"{path} is a directory")
elif stat.S_ISLNK(mode):
print(f"{path} is a symbolic link")
elif stat.S_ISSOCK(mode):
print(f"{path} is a socket")
else:
print(f"{path} is of unknown type")
此示例使用 stat 模块函数来确定文件类型。 每个函数都检查 st_mode 字段中的特定位以确定文件类型。
请注意,对于符号链接,os.stat 默认跟随它们。 使用 os.lstat 获取有关链接本身而不是其目标的信息。
检查文件权限
可以使用按位运算或 stat 模块常量从 st_mode 中提取文件权限。 此示例显示了这两种方法。
import os
import stat
file_path = "script.sh"
stats = os.stat(file_path)
mode = stats.st_mode
# Using bitwise operations
user_read = mode & stat.S_IRUSR
user_write = mode & stat.S_IWUSR
user_exec = mode & stat.S_IXUSR
print(f"Owner permissions:")
print(f" Read: {'Yes' if user_read else 'No'}")
print(f" Write: {'Yes' if user_write else 'No'}")
print(f" Execute: {'Yes' if user_exec else 'No'}")
# Using stat module constants
print("\nUsing stat module:")
print(f"Readable by owner: {bool(mode & stat.S_IRUSR)}")
print(f"Writable by owner: {bool(mode & stat.S_IWUSR)}")
print(f"Executable by owner: {bool(mode & stat.S_IXUSR)}")
第一种方法使用按位 AND 检查特定的权限位。 第二种方法展示了更简洁的版本,直接使用 stat 常量。
可以按照相同的模式对组(S_IRGRP 等)和其他用户(S_IROTH 等)权限进行类似的检查。
处理文件时间
os.stat 提供三个与时间相关的属性:st_atime(访问时间)、st_mtime(修改时间)和 st_ctime(创建/元数据更改时间)。 此示例显示了如何使用它们。
import os
import time
from datetime import datetime
file_path = "document.txt"
stats = os.stat(file_path)
# Raw timestamp values
print(f"Access time (timestamp): {stats.st_atime}")
print(f"Modification time (timestamp): {stats.st_mtime}")
print(f"Metadata change time (timestamp): {stats.st_ctime}")
# Convert to readable format
print("\nFormatted times:")
print(f"Last accessed: {datetime.fromtimestamp(stats.st_atime)}")
print(f"Last modified: {datetime.fromtimestamp(stats.st_mtime)}")
print(f"Metadata changed: {datetime.fromtimestamp(stats.st_ctime)}")
# Time comparisons
now = time.time()
hours_since_mod = (now - stats.st_mtime) / 3600
print(f"\nModified {hours_since_mod:.1f} hours ago")
这演示了如何访问原始时间戳值并将它们转换为人类可读的格式。 它还显示了如何计算时间差。
请记住,st_ctime 在不同平台上的含义不同 - 在 Windows 上表示创建时间,在类 Unix 系统上表示元数据更改时间。
比较文件
st_ino(inode)和 st_dev(设备)属性可以唯一标识文件并检测硬链接。 此示例显示了文件比较技术。
import os
def are_same_file(path1, path2):
stats1 = os.stat(path1)
stats2 = os.stat(path2)
return (stats1.st_ino == stats2.st_ino and
stats1.st_dev == stats2.st_dev)
file1 = "original.txt"
file2 = "hardlink.txt"
file3 = "copy.txt"
print(f"{file1} and {file2} same file: {are_same_file(file1, file2)}")
print(f"{file1} and {file3} same file: {are_same_file(file1, file3)}")
# Additional comparison by metadata
stats1 = os.stat(file1)
stats2 = os.stat(file3)
if (stats1.st_size == stats2.st_size and
stats1.st_mtime == stats2.st_mtime):
print("\nFiles have same size and modification time")
else:
print("\nFiles differ in size or modification time")
are_same_file 函数通过比较 inode 和设备编号来检查两个路径是否指向同一物理文件。 这会检测硬链接。
第二个比较显示了如何检查文件是否具有相同的元数据,而不一定是相同的物理文件。
处理符号链接
os.stat 默认跟随符号链接。 使用 os.lstat 获取有关链接本身而不是其目标的信息。
import os
import stat
# Create a symbolic link for demonstration
if not os.path.exists("target.txt"):
with open("target.txt", "w") as f:
f.write("Target file content")
if not os.path.exists("link.txt"):
os.symlink("target.txt", "link.txt")
# Regular stat follows symlinks
target_stats = os.stat("link.txt")
print(f"Target file size: {target_stats.st_size} bytes")
# lstat gets info about the link itself
link_stats = os.lstat("link.txt")
print(f"\nLink info:")
print(f"Size: {link_stats.st_size} bytes")
print(f"Is symlink: {stat.S_ISLNK(link_stats.st_mode)}")
# Compare the two
print("\nComparison:")
print(f"Same inode: {target_stats.st_ino == link_stats.st_ino}")
print(f"Same device: {target_stats.st_dev == link_stats.st_dev}")
此示例演示了在使用符号链接时 os.stat 和 os.lstat 之间的区别。 报告的链接大小是它包含的路径的长度。
请注意,当您需要在应用程序中专门检测和处理符号链接时,os.lstat 特别有用。
平台特定属性
某些 stat 属性是平台特定的。 此示例显示了如何在不可用时安全地访问它们,并使用回退行为。
import os
import platform
import sys
file_path = sys.executable # Using Python executable as example
stats = os.stat(file_path)
print(f"System: {platform.system()}")
print(f"File: {file_path}")
# Common attributes
print(f"\nCommon attributes:")
print(f"Size: {stats.st_size} bytes")
print(f"Mode: {oct(stats.st_mode)}")
# Platform-specific attributes
print("\nPlatform-specific attributes:")
if hasattr(stats, 'st_file_attributes'): # Windows
print(f"File attributes: {stats.st_file_attributes}")
if hasattr(stats, 'st_birthtime'): # macOS and some Unix
print(f"Birth time: {stats.st_birthtime}")
elif hasattr(stats, 'st_ctime'): # Fallback
print(f"Creation time: {stats.st_ctime}")
if hasattr(stats, 'st_blksize'): # Unix block size
print(f"Block size: {stats.st_blksize}")
print(f"Blocks: {stats.st_blocks}")
此代码演示了如何使用 hasattr 安全地检查平台特定的属性。 它显示了各种系统上可用的不同属性。
该示例还强调了在使用文件系统元数据时编写跨平台代码的重要性。
安全注意事项
- TOCTOU 风险:文件状态可能在 stat 和使用之间发生变化
- 符号链接:os.stat 默认跟随它们
- 权限检查:在执行敏感操作之前进行验证
- 错误处理:始终处理 FileNotFoundError
- 平台差异:属性可用性各不相同
最佳实践
- 使用 os.lstat:当您需要有关符号链接本身的信息时
- 检查属性是否存在:使用 hasattr 来编写跨平台代码
- 处理异常:特别是 FileNotFoundError
- 首选 pathlib:为了在新代码中更简单的路径操作
- 记录假设:关于文件类型和权限
资料来源
作者
列出所有 Python 教程。