在 Linux 系统编程中,获取和操作文件属性是一项基础且重要的任务。stat
函数作为获取文件状态信息的核心接口,为我们提供了丰富的文件元数据。本文将详细解析 stat
函数的用法、结构体成员含义,以及与文件时间戳、权限相关的实用操作。
一、stat 函数:文件信息的 "万能查询器"
stat
函数的原型非常简洁:
int stat(const char *pathname, struct stat *statbuf)
- 功能 :通过文件路径
pathname
,将该文件的状态信息填充到statbuf
指向的结构体中 - 返回值 :成功返回 0,失败返回 -1(并设置
errno
)
这个函数就像一个 "文件侦探",能帮我们获取文件的各种隐秘信息 ------ 从基本的大小、权限,到深入的 inode 编号、设备 ID 等。
二、struct stat 结构体:文件信息的 "数据字典"
struct stat
结构体是 stat
函数的核心,它包含了文件的几乎所有元数据,我们逐一解析其关键成员:
成员 | 类型 | 含义 |
---|---|---|
st_dev |
dev_t |
存储文件的设备 ID |
st_ino |
ino_t |
文件的 inode 编号(文件系统中唯一标识) |
st_mode |
mode_t |
文件类型(普通文件 / 目录 / 设备等)和权限标志 |
st_nlink |
nlink_t |
硬链接数量(ln 命令创建的链接会增加此值) |
st_uid /st_gid |
uid_t /gid_t |
文件所有者的用户 ID 和组 ID |
st_rdev |
dev_t |
特殊文件(如设备文件)的设备 ID |
st_size |
off_t |
文件大小(字节数,普通文件有效) |
st_blksize |
blksize_t |
文件系统 I/O 的块大小 |
st_blocks |
blkcnt_t |
分配的 512 字节块数量(注意:可能大于实际内容占用) |
三、文件的三个关键时间戳:atime、mtime、ctime
文件有三个重要的时间戳,常被新手混淆,这里明确区分:
-
访问时间(st_atim)
- 宏定义:
st_atime
(兼容旧版本,等价于st_atim.tv_sec
) - 含义:最后一次读取文件内容 的时间(如
cat
、less
操作会更新) - 对应命令:
acces
相关操作会修改此时间
- 宏定义:
-
修改时间(st_mtim)
- 宏定义:
st_mtime
- 含义:最后一次修改文件内容 的时间(如
echo "xxx" >> file
会更新) - 注意:内容变化才会更新,与权限无关
- 宏定义:
-
状态改变时间(st_ctim)
- 宏定义:
st_ctime
- 含义:最后一次文件状态变化 的时间,包括:
- 修改文件权限(如
chmod 755 file
) - 更改所有者(
chown
命令) - 甚至文件大小变化时也会同步更新
- 修改文件权限(如
- 宏定义:
四、文件权限与 ll 命令的对应关系
使用 ll
命令(ls -l
的别名)显示的文件信息,其实大部分来自 struct stat
:
例如一行典型的 ll
输出:
-rwxr-xr-- 1 root users 1024 7月 10 15:30 test.c
-rwxr-xr--
:对应st_mode
的权限位1
:对应st_nlink
(硬链接数)root
/users
:对应st_uid
/st_gid
(通过pwd
/grp
转换为名称)1024
:对应st_size
(文件大小)7月 10 15:30
:默认显示st_mtime
(修改时间)
五、错误处理:perror、strerror 与程序退出
当 stat
等系统调用失败时,我们需要妥善处理错误信息,常用的工具包括:
-
perror:直接打印错误描述
if (stat("test.txt", &st) == -1) { perror("stat failed"); // 输出格式:"stat failed: 错误描述" exit(EXIT_FAILURE); }
-
strerror :根据
errno
获取错误字符串#include <string.h> if (stat("test.txt", &st) == -1) { fprintf(stderr, "错误:%s\n", strerror(errno)); exit(1); }
-
程序退出 :遇到无法恢复的错误时,使用
exit()
或_exit()
终止程序,避免后续错误操作。
六、实用示例:用 stat 实现简易文件信息查看器
下面是一个简单的程序,演示如何使用 stat
函数获取并打印文件关键信息:
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "用法:%s <文件名>\n", argv[0]);
return 1;
}
struct stat st;
if (stat(argv[1], &st) == -1) {
perror("获取文件信息失败");
exit(EXIT_FAILURE);
}
printf("文件: %s\n", argv[1]);
printf("大小: %lld 字节\n", (long long)st.st_size);
printf("权限: 0%o\n", st.st_mode & 0777); // 提取权限位
printf("硬链接数: %d\n", st.st_nlink);
printf("最后访问: %s", ctime(&st.st_atime));
printf("最后修改: %s", ctime(&st.st_mtime));
printf("最后状态改变: %s", ctime(&st.st_ctime));
return 0;
}
总结
stat
函数是 Linux 系统编程中操作文件属性的基础,理解它的结构体成员和返回值,能帮助我们更深入地掌握文件系统的工作机制。无论是获取文件大小、判断文件类型,还是监控文件的修改状态,stat
都能胜任。结合 perror
、strerror
等错误处理函数,可以写出更健壮的文件操作程序。
下次当你使用 ll
命令查看文件信息时,不妨想想:这些数据其实都来自 struct stat
结构体呢!