1. 课程概述与目标
- 核心主题:学习如何获取文件的详细属性(File Attributes)。
- 背景:上一节课学习了访问目录,但仅能获取文件名字和文件类型。
- 目标:实现类似
ls -al命令的效果。- 展示文件的时间戳(Time Stamp)。
- 展示文件的读写权限(Read/Write/Execute Permissions)。
- 展示文件大小、所有者等细致属性。
- 关键技术:借助
stat系列函数获取文件元数据。
2. 核心函数:stat、lstat、fstat
这三个函数用于获取文件状态信息,需包含以下头文件(缺一不可):
<sys/types.h><sys/stat.h><unistd.h>
2.1 函数原型与参数
查阅手册命令:man 2 stat
-
stat- 原型:
int stat(const char *pathname, struct stat *buf); - 参数 1:
pathname(文件路径字符串)。 - 参数 2:
buf(struct stat结构体指针)。- 注意:这是一个“空盘子”模型。调用前需定义结构体变量,传入其地址。函数执行成功后,结构体被填充数据。
- 传值方式:指针传递(地址传递),用于回传数据。
- 返回值:
- 成功:返回
0。 - 失败:返回
-1,并设置errno。
- 成功:返回
- 原型:
-
lstat- 原型:
int lstat(const char *pathname, struct stat *buf); - 区别:与
stat功能基本一致,但在处理**符号链接(Symbolic Link)**时不同。stat:会跟随符号链接,获取指向目标文件的属性。lstat:不跟随符号链接,获取符号链接文件本身的属性。
- 应用场景:需要区分链接文件本身与其指向文件时使用。
- 原型:
-
fstat- 原型:
int fstat(int fd, struct stat *buf); - 区别:第一个参数是文件描述符(File Descriptor, fd),而非路径字符串。
- 应用场景:当文件已经通过
open打开,拥有 fd 时使用,效率更高。
- 原型:
3. struct stat 结构体分析
函数执行成功后,通过 struct stat 结构体成员获取具体信息。常用成员如下:
st_mode:文件类型和访问权限(16 位值,核心重点)。st_size:文件大小(字节)。st_mtime:最后一次修改时间(Time Stamp)。- 其他成员:所有者 ID、组 ID 等(本课程暂不深入)。
注意:文件属性具有时效性。获取结构体后,若文件被修改,结构体信息不会自动同步,需注意信息的实时性。
4. st_mode 成员深度解析
st_mode 是一个 16 位的二进制值,每一位代表不同含义。通常通过预定义的**宏(Macro)**进行判断,而非直接操作二进制。
4.1 位分布含义
- Bit 12-15:标记文件类型(File Type)。
- Bit 9-11:特殊权限位(SetUID, SetGID, Sticky Bit)。
- Bit 6-8:所有者(User)权限。
- Bit 3-5:所属组(Group)权限。
- Bit 0-2:其他用户(Other)权限。
4.2 文件类型判断(Bit 12-15)
共有 7 种常见文件类型,通过宏进行位运算判断。
-
判断方式:
- 位与运算(Bitwise AND):
st_mode & S_IFMT得到类型掩码,再与具体类型宏比较。 - 专用宏判断:直接使用如
S_ISREG(st_mode)等宏(推荐)。
- 位与运算(Bitwise AND):
-
常见类型宏:
S_IFREG:普通文件(Regular File),ls显示为-。S_IFDIR:目录(Directory),ls显示为d。S_IFLNK:符号链接(Symbolic Link),ls显示为l。S_IFCHR:字符设备(Character Device),ls显示为c。S_IFBLK:块设备(Block Device),ls显示为b。S_IFIFO:管道(FIFO/Pipe),ls显示为p。S_IFSOCK:套接字(Socket),ls显示为s。
-
宏的数值形式:通常以八进制表示。
- 例如:类型掩码部分对应八进制的高位,权限部分对应低三位(如
0777)。
- 例如:类型掩码部分对应八进制的高位,权限部分对应低三位(如
4.3 权限判断(Bit 0-11)
权限分为三组(User, Group, Other),每组包含 Read(r), Write(w), Execute(x)。
- 权限宏:
S_IRUSR(User Read),S_IWUSR(User Write),S_IXUSR(User Execute)。S_IRGRP,S_IWGRP,S_IXGRP。S_IROTH,S_IWOTH,S_IXOTH。
- 数值对应:
r= 4w= 2x= 1- 每组最大值为 7 (4+2+1)。
5. 代码实现 walkthrough:模仿 ls -l
5.1 基础框架搭建
- 包含头文件:
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> // 用于 open 函数 #include <time.h> // 用于时间格式化 - 获取文件名:通过命令行参数
argv[1]获取目标文件路径。 - 定义结构体:
struct stat st; - 调用函数:
if (stat(argv[1], &st) == -1) { perror("stat error"); return -1; }
5.2 实现文件类型输出
使用 switch-case 结构判断 st_mode 的文件类型部分。
- 逻辑:先通过位运算提取类型部分,再匹配宏。
- 代码示例:
switch (st.st_mode & S_IFMT) { case S_IFREG: printf("-"); break; case S_IFDIR: printf("d"); break; case S_IFLNK: printf("l"); break; case S_IFCHR: printf("c"); break; case S_IFBLK: printf("b"); break; case S_IFIFO: printf("p"); break; case S_IFSOCK: printf("s"); break; default: printf("?"); break; }
5.3 实现权限位输出(rwx)
权限共有 9 个位(User 3 位 + Group 3 位 + Other 3 位)。
-
算法逻辑:
- 循环 9 次(或从高位到低位遍历)。
- 利用位移(Shift)和取模(Modulo)运算确定当前位对应
r、w还是x。 - 视频中的特定算法:
- 定义循环变量
n从 8 递减到 0。 - 利用
n % 3的结果判断权限类型:- 余数为 2:对应
r(Read) - 余数为 1:对应
w(Write) - 余数为 0:对应
x(Execute)
- 余数为 2:对应
- 注意:由于位移方向与权限顺序的对应关系,需仔细验证位移量。视频中提到通过
n的变化规律挂钩rwx顺序。
- 定义循环变量
- 判断有无权限:
- 将
st_mode左移或右移相应位数后与1进行位与运算。 - 若结果为 1,打印对应字符(r/w/x);若为 0,打印
-。
- 将
-
简化逻辑:
也可以直接分别与S_IRUSR,S_IWUSR等宏进行位与判断,代码可读性更高。// 示例:判断所有者读权限 if (st.st_mode & S_IRUSR) printf("r"); else printf("-");
5.4 实现文件大小输出
- 直接访问
st_size成员。 - 格式化为十进制整数输出。
printf(" %ld", st.st_size);
5.5 实现时间戳输出
- 成员:
st_mtime(最后一次修改时间)。 - 类型:
time_t。 - 格式化函数:
ctime()。- 原型:
char *ctime(const time_t *timep); - 用法:
printf("%s", ctime(&st.st_mtime)); - 注意:
ctime返回的字符串包含换行符,需注意输出格式控制。
- 原型:
- 头文件:需包含
<time.h>。
6. 调试与常见问题记录
6.1 编译与运行问题
- 头文件缺失:
- 使用
open函数未包含<fcntl.h>会导致O_RDONLY等宏未定义。 - 使用时间函数未包含
<time.h>会导致ctime报错。 - 解决:根据编译器报错补充对应头文件。
- 使用
- open 函数标志位:
- 文件 I/O (
open) 与标准 I/O (fopen) 标志不同。 open需使用O_RDONLY,O_WRONLY等宏,而非"r","w"字符串。
- 文件 I/O (
- switch-case 语法错误:
- Case 标签必须是整型常量表达式。确保使用的宏(如
S_IFREG)已正确包含头文件。 - 视频中出现
case位置报错,原因是宏未定义或拼写错误(如S_IFLNK写错)。
- Case 标签必须是整型常量表达式。确保使用的宏(如
6.2 逻辑验证
- 文件类型测试:
- 普通文件(
.c):应输出-。 - 目录(
.):应输出d。 - 链接文件:
stat会显示目标文件类型,lstat显示l。
- 普通文件(
- 权限位测试:
- 通过
chmod修改文件权限后运行程序,验证输出是否同步变化。
- 通过
- 输出格式对齐:
ls -l输出有固定格式,自行实现时需注意空格和换行处理,避免信息堆叠。
7. 总结与建议
- 函数选择:
- 一般场景使用
stat。 - 涉及符号链接分析使用
lstat。 - 已打开文件描述符场景使用
fstat。
- 一般场景使用
- 位运算技巧:
- 理解
st_mode的位分布是核心。 - 优先使用系统提供的宏(
S_ISxxx,S_IRxxx)而非硬编码数值,增强可移植性。
- 理解
- 扩展学习:
- 查阅
man 2 stat了解所有结构体成员。 - 尝试实现完整的
ls -l输出(包括硬链接数、所有者名、组名等,需结合pwd.h和grp.h)。 - 研究特殊权限位(SetUID, Sticky Bit)的含义与判断。
- 查阅