• stat函数



    Linux 下可以使用 stat 命令查看文件的属性,其实这个命令内部就是通过调用 stat()函数来获取文件属性的,stat 函数是 Linux 中的系统调用,用于获取文件相关的信息,函数原型如下所示):

    #include 
    #include 
    #include 
    int stat(const char *pathname, struct stat *buf);
    
    • 1
    • 2
    • 3
    • 4

    首先使用该函数需要包含以及这三个头文件。
    函数参数及返回值含义如下:
    pathname:用于指定一个需要查看属性的文件路径。
    buf:struct stat 类型指针,用于指向一个 struct stat 结构体变量。调用 stat 函数的时候需要传入一个 struct stat 变量的指针,获取到的文件属性信息就记录在 struct stat 结构体中。
    返回值:成功返回 0;失败返回-1,并设置 error。

    (一)struct stat 结构体

    structstat 是内核定义的一个结构体,在头文件中申明,所以可以在应用层使用,这个结构体中的所有元素加起来构成了文件的属性信息,结构体内容如下所示:

    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; /* 文件状态最后被改变的时间 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    st_dev:该字段用于描述此文件所在的设备。不常用,可以不用理会。
    st_ino:文件的 inode 编号。
    st_mode:该字段用于描述文件的模式,譬如文件类型、文件权限都记录在该变量中。
    st_nlink:该字段用于记录文件的硬链接数,也就是为该文件创建了多少个硬链接文件。链接文件可以分为软链接(符号链接)文件和硬链接文件。
    st_uid、st_gid:此两个字段分别用于描述文件所有者的用户 ID 以及文件所有者的组 ID。
    st_rdev:该字段记录了设备号,设备号只针对于设备文件,包括字符设备文件和块设备文件,不用理会。
    st_size:该字段记录了文件的大小(逻辑大小),以字节为单位。
    st_atim、st_mtim、st_ctim:此三个字段分别用于记录文件最后被访问的时间、文件内容最后被修改的时间以及文件状态最后被改变的时间,都是 struct timespec 类型变量。

    (二) st_mode 变量

    st_mode 是 structstat 结构体中的一个成员变量,是一个 32 位无符号整形数据,该变量记录了文件的类型、文件的权限这些信息,其表示方法如下所示:在这里插入图片描述
    O 对应的 3 个 bit 位用于描述其它用户的权限;
    G 对应的 3 个 bit 位用于描述同组用户的权限;
    U 对应的 3 个 bit 位用于描述文件所有者的权限;
    S 对应的 3 个 bit 位用于描述文件的特殊权限。
    这些 bit 位表达内容与 open 函数的 mode 参数相对应,这里不再重述。同样,在 mode 参数中表示权限的宏定义,在这里也是可以使用的,这些宏定义如下(以下数字使用的是八进制方式表示):

    S_IRWXU 00700 owner has read, write, and execute permission
    S_IRUSR 00400 owner has read permission
    S_IWUSR 00200 owner has write permission
    S_IXUSR 00100 owner has execute permission
    S_IRWXG 00070 group has read, write, and execute permission
    S_IRGRP 00040 group has read permission
    S_IWGRP 00020 group has write permission
    S_IXGRP 00010 group has execute permission
    S_IRWXO 00007 others (not in group) have read, write, and execute permission
    S_IROTH 00004 others have read permission
    S_IWOTH 00002 others have write permission
    S_IXOTH 00001 others have execute permission
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    譬如,判断文件所有者对该文件是否具有可执行权限,可以通过以下方法测试(假设 st 是 structstat 类型变量):

    if (st.st_mode & S_IXUSR) {
    //有权限
    } else {
    //无权限
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里我们重点来看看“文件类型”这 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(管道文件)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意上面这些数字使用的是八进制方式来表示的,在 C 语言中,八进制方式表示一个数字需要在数字前面添加一个 0(零)。所以由上面可知,当“文件类型”这 4 个 bit 位对应的数字是 14(八进制)时,表示该文件是一个套接字文件、当“文件类型”这 4 个 bit 位对应的数字是 12(八进制)时,表示该文件是一个链接文件、当“文件类型”这 4 个 bit 位对应的数字是 10(八进制)时,表示该文件是一个普通文件等。

    所以通过 st_mode 变量判断文件类型就很简单了,如下(假设 st 是 struct stat 类型变量):

    /* 判断是不是普通文件 */
    if ((st.st_mode & S_IFMT) == S_IFREG) {
    /* 是 */
    }
    /* 判断是不是链接文件 */
    if ((st.st_mode & S_IFMT) == S_IFLNK) {
    /* 是 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    S_IFMT 宏是文件类型字段位掩码:S_IFMT 0170000
    除了这样判断之外,我们还可以使用 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    有了这些宏之后,就可以通过如下方式来判断文件类型了:

    /* 判断是不是普通文件 */
    if (S_ISREG(st.st_mode)) {
    /* 是 */
    }
    /* 判断是不是目录 */
    if (S_ISDIR(st.st_mode)) {
    /* 是 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (三) struct timespec 结构体

    结构体定义头文件中,是 Linux 系统中时间相关的结构体。应用程序中包含了头文件,就可以在应用程序中使用该结构体了,结构体内容如下所示:

    struct timespec
    {
     time_t tv_sec; /* 秒 */
     syscall_slong_t tv_nsec; /* 纳秒 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    struct timespec 结构体中只有两个成员变量,一个秒(tv_sec)、一个纳秒(tv_nsec),time_t 其实指的就是 long int 类型,所以由此可知,该结构体所表示的时间可以精确到纳秒,当然,对于文件的时间属性来说,并不需要这么高的精度,往往只需精确到秒级别即可。

    在 Linux 系统中,time_t 时间指的是一个时间段,从某一个时间点到某一个时间点所经过的秒数,譬如对于文件的三个时间属性来说,指的是从过去的某一个时间点(这个时间点是一个起始基准时间点)到文件最后被访问、文件内容最后被修改、文件状态最后被改变的这个时间点所经过的秒数。time_t 时间在 Linux下被称为日历时间,structstat 结构体中包含了三个文件相关的时间属性,但这里得到的仅仅只是以秒+微秒为单位的时间值,譬如可
    以通过 localtime()/localtime_r()或者 strftime()来得到更利于我们查看的时间表达方式。

    练习

    (1)获取文件的 inode 节点编号以及文件大小,并将它们打印出来。
    (2)获取文件的类型,判断此文件对于其它用户(Other)是否具有可读可写权限。
    (3)获取文件的时间属性,包括文件最后被访问的时间、文件内容最后被修改的时间以及文件状态最后被改变的时间,并使用字符串形式将其打印出来,包括时间和日期、表示形式自定。

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(void) 
    {
    	 struct stat file_stat;
    	 int ret;
    	 
    	 /* 获取文件属性 */
    	 ret = stat("./test_file", &file_stat);
    	 if (-1 == ret) 
    	 {
    		 perror("stat error");
    		 exit(-1);
    	 }
    	 
    	 /* 打印文件大小和 inode 编号 */
    	 printf("file size: %ld bytes\n"
    	 "inode number: %ld\n", file_stat.st_size,
    	 file_stat.st_ino);
    	 exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    Docker基础操作
    Deno 中使用 @typescript/vfs 生成 DTS 文件
    判断2叉树是否为对称树(C#)
    原创Python文件头注释块
    Jackson公司蛋白质电印迹方法&确认蛋白质转移
    【算法6】移除元素
    js实现深拷贝的几种方式
    人工智能基础知识
    【深度学习】卷积神经网络应用之双阶段目标检测|R-CNN、SPP-Net、Fast-RCNN、Faster-RCNN
    NIFI从Oracle11G同步数据到Mysql_亲测可用_解决数据重复_数据跟源表不一致的问题---大数据之Nifi工作笔记0065
  • 原文地址:https://blog.csdn.net/Dustinthewine/article/details/126673326