目录
1 文件有哪些属性
2 stat函数
2.1 stat函数简介
2.2 struct stat 结构体
2.3 struct timespec 结构体
2.4 示例程序
3 fstat 和 lstat 函数
3.1 fstat 函数
3.2 lstat 函数
1 文件有哪些属性
Linux文件属性是对文件和目录的元数据描述,包括文件类型、权限设置、所有者、所属组、文件大小、修改时间、状态更改时间、访问时间、inode号、链接数以及文件系统等,这些属性通过命令如ls -l
和stat
可以查看,它们定义了文件的访问权限和存储细节,是Linux系统管理文件的基础。
2 stat函数
2.1 stat函数简介
Linux 下可以使用 stat 命令查看文件的属性,命令内部就是通过调用 stat()函数来获取文件属性的, stat 函数是 Linux 中的系统调用,用于获取文件相关的信息,函数原型如下所示(可通过"man 2 stat"命令查看) :
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
pathname: 用于指定一个需要查看属性的文件路径。
buf: struct stat 类型指针,用于指向一个 struct stat 结构体变量。调用 stat 函数的时候需要传入一个 structstat 变量的指针, 获取到的文件属性信息就记录在 struct stat 结构体中,结构体含义见2.2节。
返回值: 成功返回 0;失败返回-1,并设置 error。
2.2 struct stat 结构体
struct stat
结构体用于存储由stat()
系统调用返回的文件信息,在<sys/stat.h>头文件中申明,结构体内容如下所示:
struct stat
{
dev_t st_dev; /* 文件所在设备的 ID */
ino_t st_ino; /* 文件对应 inode 节点编号 */
mode_t st_mode; /* 文件对应的模式,譬如文件类型、文件权限都记录在该变量中 */
nlink_t st_nlink; /* 文件的链接数 */
uid_t st_uid; /* 文件所有者的用户 ID */
gid_t st_gid; /* 文件所有者的组 ID */
dev_t st_rdev; /* 设备号(指针对设备文件) */
off_t st_size; /* 文件大小(以字节为单位) */
blksize_t st_blksize; /* 文件内容存储的块大小 */
blkcnt_t st_blocks; /* 文件内容所占块数 */
struct timespec st_atim; /* 文件最后被访问的时间 */
struct timespec st_mtim; /* 文件内容最后被修改的时间 */
struct timespec st_ctim; /* 文件状态最后被改变的时间 */
};
关于st_mode参数可参考文章4.1节:嵌入式Linux系统编程 — 1.1 文件I/O基础-CSDN博客
t_mode参数中包含“文件类型”,“文件类型”用4 个 bit 位表示,并有相应的宏定义, 这 4 个 bit 位用于描述该文件的类型,譬如该文件是普通文件、还是链接文件、亦或者是一个目录等,那么就可以通过这 4 个 bit 位数据判断出来,如下所示:
S_IFSOCK 0140000 socket(套接字文件)
S_IFLNK 0120000 symbolic link(链接文件)
S_IFREG 0100000 regular file(普通文件)
S_IFBLK 0060000 block device(块设备文件)
S_IFDIR 0040000 directory(目录)
S_IFCHR 0020000 character device(字符设备文件)
S_IFIFO 0010000 FIFO(管道文件)
上面这些数字使用的是八进制方式来表示的,在 C 语言中,八进制方式表示一个数字需要在数字前面添加一个 0。所以由上面可知,当“文件类型”这 4 个 bit 位对应的数字是 14(八进制)时,表
示该文件是一个套接字文件。
所以通过 st_mode 变量可以判断文件类型,如下(假设 st 是 struct stat 类型变量) :
/* 判断是不是普通文件 */
if ((st.st_mode & S_IFMT) == S_IFREG) {
/* 是 */
}
/* 判断是不是链接文件 */
if ((st.st_mode & S_IFMT) == S_IFLNK) {
/* 是 */
}
除了这样判断之外,我们还可以使用 Linux 系统封装好的宏来进行判断,如下所示(m 是 st_mode变量) :
S_ISREG(m) #判断是不是普通文件,如果是返回 true,否则返回 false
S_ISDIR(m) #判断是不是目录,如果是返回 true,否则返回 false
S_ISCHR(m) #判断是不是字符设备文件,如果是返回 true,否则返回 false
S_ISBLK(m) #判断是不是块设备文件,如果是返回 true,否则返回 false
S_ISFIFO(m) #判断是不是管道文件,如果是返回 true,否则返回 false
S_ISLNK(m) #判断是不是链接文件,如果是返回 true,否则返回 false
S_ISSOCK(m) #判断是不是套接字文件,如果是返回 true,否则返回 false
有了这些宏之后,就可以通过如下方式来判断文件类型了:
/* 判断是不是普通文件 */
if (S_ISREG(st.st_mode)) {
/* 是 */
}
/* 判断是不是目录 */
if (S_ISDIR(st.st_mode)) {
/* 是 */
}
2.3 struct timespec 结构体
该结构体定义在<time.h>头文件中, 是 Linux 系统中时间相关的结构体,结构体内容如下所示:
struct timespec
{
time_t tv_sec; /* 秒 */
syscall_slong_t tv_nsec; /* 纳秒 */
};
tv_sec
:表示自1970年1月1日(UTC时间)以来的秒数,这是一个time_t
类型的值。tv_nsec
:表示tv_sec
之后的纳秒数,范围从0到999,999,999。这个字段用于提供比秒更精确的时间度量。
2.4 示例程序
下面的代码通过命令行接收一个文件名作为参数,并使用stat
系统调用来获取该文件的状态信息。
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
// 函数用于将time_t类型的时间转换为可读的字符串格式并打印
void print_time(time_t time) {
struct tm *tm_info; // 用于存储本地时间信息的结构体
char buffer[26]; // 用于存储格式化时间的字符串
tm_info = localtime(&time); // 将time_t转换为本地时间
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info); // 格式化时间
printf("%s\n", buffer); // 打印格式化后的时间字符串
}
int main(int argc, char *argv[]) {
struct stat file_stat; // 用于存储文件状态的结构体
// 检查命令行参数数量,确保有文件名提供
if (argc < 2) {
printf("Usage: %s <filename>\n", argv[0]);
return 1;
}
// 使用stat系统调用来获取文件状态
if (stat(argv[1], &file_stat) == -1) {
perror("Error getting file status"); // 打印错误信息
return 1;
}
// 打印文件的inode节点编号和文件大小
printf("Inode: %ld\n", (long)file_stat.st_ino);
printf("Size: %lld bytes\n", (long long)file_stat.st_size);
// 判断文件类型并打印
switch (file_stat.st_mode & S_IFMT) {
case S_IFREG:
printf("Regular file\n");
break;
case S_IFDIR:
printf("Directory\n");
break;
// 可以添加更多的文件类型判断
default:
printf("Unknown type\n");
}
// 检查其他用户的权限
if (file_stat.st_mode & S_IROTH && file_stat.st_mode & S_IWOTH)
printf("Other users have read and write permissions\n");
else
printf("Other users do not have read and write permissions\n");
// 打印文件的时间属性
printf("Last accessed: ");
print_time(file_stat.st_atime);
printf("Last modified: ");
print_time(file_stat.st_mtime);
printf("Status was last changed: ");
print_time(file_stat.st_ctime);
printf("\n");
return 0;
}
程序定义了一个print_time
函数,用于将time_t
类型的时间戳转换为易读的日期和时间字符串格式,并将其打印出来。在main
函数中,首先检查是否提供了足够的命令行参数,然后获取文件状态,打印文件的inode编号、大小、类型以及其他用户的读写权限。最后,程序调用print_time
函数打印文件的最后访问时间、最后修改时间和状态最后改变时间。程序运行结果如下:
3 fstat 和 lstat 函数
3.1 fstat 函数
除了 stat 函数之外,还可以使用 fstat 和 lstat 两个系统调用来获取文件属性信息。
fstat 与 stat 区别在于, stat 是从文件名出发得到文件属性信息,不需要先打开文件;而 fstat 函数则是从文件描述符出发得到文件属性信息,所以使用 fstat 函数之前需要先打开文件得到文件描述符。
#include <sys/stat.h>
#include <unistd.h>
int fstat(int filedes, struct stat *buf);
filedes
:指定要获取状态的文件描述符。buf
:指向一个struct stat
结构体的指针,该结构体用于接收文件的状态信息。
fstat 函数使用示例如下:
#include <stdio.h>
#include <sys/stat.h>
int main() {
FILE *file = fopen("example.txt", "w"); // 使用标准I/O库打开文件
if (file == NULL) {
perror("Failed to open file");
return 1;
}
struct stat file_stat;
if (fstat(fileno(file), &file_stat) == -1) { // 使用文件流获取文件描述符并获取状态信息
perror("Failed to get file status");
fclose(file);
return 1;
}
printf("File size: %lld bytes\n", (long long)file_stat.st_size);
fclose(file); // 使用标准I/O库关闭文件
return 0;
}
代码使用 fstat
函数通过 fopen
得到的文件流获取文件状态信息,包括文件大小。然后,它打印出文件的大小。运行结果如下:
3.2 lstat 函数
lstat()与 stat、 fstat 的区别在于,对于符号链接文件, stat、 fstat 查阅的是符号链接文件所指向的文件对应的文件属性信息,而 lstat 查阅的是符号链接文件本身的属性信息。lstat 函数原型如下所示:
#include <sys/stat.h>
#include <unistd.h>
int lstat(const char *path, struct stat *buf);
path
:指定要获取状态的文件或链接的路径。buf
:指向一个struct stat
结构体的指针,该结构体用于接收文件或链接的状态信息。