stat 是一个非常重要的系统调用,它用于获取文件或文件系统的状态信息,通过 stat,你的程序可以知道一个文件的大小、权限、所有者、最后修改时间等各种属性。

(图片来源网络,侵删)
核心概念
stat 函数通过一个指向 stat 结构体的指针来返回文件信息,你需要做的是:
- 包含头文件:
sys/stat.h和unistd.h。 - 定义
stat结构体变量:struct stat file_stat;。 - 调用
stat函数: 将文件路径和结构体变量的地址传给它。 - 检查返回值: 如果函数返回
0,表示成功;返回-1表示失败,此时可以通过errno变量查看具体错误原因。 - 访问结构体成员: 成功后,文件的所有信息都存储在
file_stat变量中,你可以通过它的成员来获取。
函数原型
#include <sys/stat.h> #include <unistd.h> // 包含了 ssize_t 等类型定义 int stat(const char *pathname, struct stat *statbuf);
参数:
pathname: 要查询的文件的路径名,可以是相对路径或绝对路径。statbuf: 指向struct stat结构体的指针。stat函数会将文件信息填充到这个结构体中。
返回值:
- 成功: 返回
0。 - 失败: 返回
-1,并设置errno来指示错误。
stat 结构体详解
struct stat 是核心,它定义了文件的各种属性,以下是其主要成员(在 64 位系统上,通常以 64 如 st_mode_t):

(图片来源网络,侵删)
struct stat {
dev_t st_dev; // 文件的设备 ID
ino_t st_ino; // inode 号
mode_t st_mode; // 文件类型和权限 ( S_IFREG | 0644)
nlink_t st_nlink; // 硬链接数
uid_t st_uid; // 用户 ID
gid_t st_gid; // 组 ID
dev_t st_rdev; // 设备 ID (如果是特殊文件)
off_t st_size; // 文件大小(字节),以字节为单位
blksize_t st_blksize; // 文件系统 I/O 的块大小
blkcnt_t st_blocks; // 分配的 512 字节块的数量
time_t st_atime; // 最后访问时间 (access time)
time_t st_mtime; // 最后修改内容的时间 (modification time)
time_t st_ctime; // 最后状态改变的时间 (status change time)
};
关键成员解释:
-
st_mode: 这是一个非常重要的成员,它包含了文件的类型和权限,我们可以用宏来解析它:- 文件类型:
S_ISREG(m): 是否是普通文件S_ISDIR(m): 是否是目录S_ISLNK(m): 是否是符号链接S_ISBLK(m): 是否是块设备文件S_ISCHR(m): 是否是字符设备文件S_ISFIFO(m): 是否是命名管道 (FIFO)S_ISSOCK(m): 是否是套接字 (socket)
- 文件权限:
S_IRUSR(用户读),S_IWUSR(用户写),S_IXUSR(用户执行)S_IRGRP(组读),S_IWGRP(组写),S_IXGRP(组执行)S_IROTH(其他读),S_IWOTH(其他写),S_IXOTH(其他执行)st_mode & 0777可以提取出文件的权限位(如0644)。
- 文件类型:
-
st_size: 文件的大小(字节),对于目录,这个值通常没有太大意义。 -
st_uid,st_gid: 文件所有者和所属组的 ID,通常需要结合<sys/types.h>和<pwd.h>/<grp.h>中的函数getpwuid()和getgrgid()来转换成可读的用户名和组名。
(图片来源网络,侵删) -
st_atime,st_mtime,st_ctime: 三个时间戳。st_atime: Access Time,最后一次访问(读取)文件的时间,用cat命令查看文件内容,atime就会更新。st_mtime: Modification Time,最后一次修改文件内容的时间,用echo或vim编辑文件,mtime就会更新,这是最常用的时间戳。st_ctime: Change Time,最后一次改变文件状态(如权限、所有者)或 inode 内容的时间,用chmod或chown命令,ctime就会更新。
完整示例代码
下面是一个完整的示例,它会打印出指定文件的几乎所有 stat 信息。
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <pwd.h> // 用于 getpwuid
#include <grp.h> // 用于 getgrgid
#include <stdlib.h> // 用于 exit
// 辅助函数:将 st_mode 转换为可读的权限字符串 (如 rwxr-xr--)
void print_permissions(mode_t mode) {
printf((mode & S_IRUSR) ? "r" : "-");
printf((mode & S_IWUSR) ? "w" : "-");
printf((mode & S_IXUSR) ? "x" : "-");
printf((mode & S_IRGRP) ? "r" : "-");
printf((mode & S_IWGRP) ? "w" : "-");
printf((mode & S_IXGRP) ? "x" : "-");
printf((mode & S_IROTH) ? "r" : "-");
printf((mode & S_IWOTH) ? "w" : "-");
printf((mode & S_IXOTH) ? "x" : "-");
}
// 辅助函数:将 st_mode 转换为文件类型字符串
void print_file_type(mode_t mode) {
if (S_ISREG(mode)) printf("普通文件");
else if (S_ISDIR(mode)) printf("目录");
else if (S_ISLNK(mode)) printf("符号链接");
else if (S_ISBLK(mode)) printf("块设备");
else if (S_ISCHR(mode)) printf("字符设备");
else if (S_ISFIFO(mode)) printf("FIFO/命名管道");
else if (S_ISSOCK(mode)) printf("套接字");
else printf("未知类型");
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "用法: %s <文件路径>\n", argv[0]);
return 1;
}
const char *filepath = argv[1];
struct stat file_stat;
// 调用 stat 函数
if (stat(filepath, &file_stat) == -1) {
// 打印错误信息
perror("stat 调用失败");
return 1;
}
// 打印文件信息
printf("文件: %s\n", filepath);
printf("----------------------------------------\n");
printf("设备 ID: %ld\n", (long)file_stat.st_dev);
printf("inode 号: %ld\n", (long)file_stat.st_ino);
printf("类型: ");
print_file_type(file_stat.st_mode);
printf("\n");
printf("权限: ");
print_permissions(file_stat.st_mode);
printf("\n");
printf("硬链接数: %ld\n", (long)file_stat.st_nlink);
// 获取用户名和组名
struct passwd *pw = getpwuid(file_stat.st_uid);
struct group *gr = getgrgid(file_stat.st_gid);
printf("所有者: %s (ID: %d)\n", pw ? pw->pw_name : "未知", file_stat.st_uid);
printf("所属组: %s (ID: %d)\n", gr ? gr->gr_name : "未知", file_stat.st_gid);
printf("文件大小: %ld 字节\n", (long)file_stat.st_size);
printf("块大小: %ld 字节\n", (long)file_stat.st_blksize);
printf("块数量: %ld\n", (long)file_stat.st_blocks);
// 打印时间戳
char time_buf[80];
printf("最后访问时间: %s", ctime(&file_stat.st_atime));
printf("最后修改时间: %s", ctime(&file_stat.st_mtime));
printf("最后状态改变时间: %s", ctime(&file_stat.st_ctime));
return 0;
}
如何编译和运行:
- 将上述代码保存为
file_stat.c。 - 创建一个测试文件
test.txt并写入一些内容:echo "Hello, stat function!" > test.txt chmod 644 test.txt
- 编译代码:
gcc file_stat.c -o file_stat
- 运行程序:
./file_stat test.txt
预期输出示例:
文件: test.txt
----------------------------------------
设备 ID: 2050
inode 号: 131073
类型: 普通文件
权限: rw-r--r--
硬链接数: 1
所有者: your_username (ID: 1000)
所属组: your_group (ID: 1000)
文件大小: 21 字节
块大小: 4096 字节
块数量: 8
最后访问时间: Wed Oct 26 10:30:00 2025
最后修改时间: Wed Oct 26 10:30:00 2025
最后状态改变时间: Wed Oct 26 10:30:00 2025
stat、lstat 和 fstat 的区别
在 Linux 中,有三个非常相似的函数,理解它们的区别很重要:
| 函数 | 描述 | 区别 |
|---|---|---|
stat() |
获取文件路径指向的最终目标文件的信息。 | 如果路径是符号链接,它会解引用(dereference),获取链接所指向的文件的信息。 |
lstat() |
获取文件路径本身(符号链接文件)的信息。 | 如果路径是符号链接,它不会解引用,而是获取这个链接文件本身的信息(比如链接本身的大小),这是它与 stat 的关键区别。 |
fstat() |
获取一个已打开的文件描述符 (fd) 所对应的文件的信息。 |
它不使用文件路径,而是使用由 open() 或 creat() 等函数返回的文件描述符。 |
lstat 示例:
假设你有一个符号链接 mylink.txt 指向 test.txt。
ln -s test.txt mylink.txt
用 stat 查看 mylink.txt:
stat("mylink.txt", &stat_buf); // 会获取 test.txt 的信息
用 lstat 查看 mylink.txt:
lstat("mylink.txt", &stat_buf); // 会获取 mylink.txt 这个链接文件本身的信息
// st_size 会很小,8 (因为 "test.txt" 是 8 个字符)
stat是获取文件状态信息的基石。- 它的核心是
struct stat结构体,你需要熟练掌握其主要成员的含义。 st_mode中的宏(S_ISREG,S_IRUSR等)是解析文件类型和权限的关键。- 当处理符号链接时,请务必使用
lstat来获取链接本身的信息,避免意外的解引用。
