【Linux文件IO】Linux中文件属性与目录操作的API介绍和用法

ops/2025/4/1 3:19:33/

Linux中文件属性与目录操作的API介绍和用法

  • 一、文件属性
    • 1、获取文件属性信息的函数接口
    • 2、文件的设备号
      • 示例代码:
    • 3、类型与权限
    • 4、其他简单文件信息
  • 二、目录操作
    • 1、基本概念
    • 2、相关的接口函数
      • (1) 打开目录
      • (2) 读取目录(重点)
        • 示例代码:
      • (3) 新建目录
        • 示例代码:
      • (4) 删除目录
        • 示例代码:
      • (5) 关闭目录

在应用开发中,经常要获取文件的属性,例如:文件的类型、大小、权限、设备号、最近修改时间等等,比如网络传输文件时,一般都需要先传递文件的属性,等准备妥善了再开始传输文件的真实内容。因此,熟悉文件属性的细节,并熟练获取这些信息的方式至关重要。

一、文件属性

1、获取文件属性信息的函数接口

如下函数可以获取指定文件的属性:
在这里插入图片描述
注意:
1.以上三个函数的功能一样,区别如下:

  • stat针对文件名获取其信息。
  • fstat针对文件描述符获取其信息。
  • lstat可以获取软连接文件(快捷方式)本身的属性。

2.这几个函数获取了文件的属性之后,会将这些信息存入一个称为stat的结构体中(与函数同名),该结构体的细节如下:

struct stat
{dev_t     st_dev;    // 本文件所在的设备的设备号,适用于非设备文件ino_t     st_ino;    // i节点号,相当于身份证号码mode_t    st_mode;   // 文件类型 + 文件权限nlink_t   st_nlink;  // 文件的别名的数目uid_t     st_uid;    // 文件所有者IDgid_t     st_gid;    // 文件所在组IDdev_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;  // 最近状态改变时间,比如修改了文件的权限的时间};// 其中,时间戳结构体的细节是:
struct timespec
{long    tv_sec;   /* 秒 */long    tv_nsec;  /* 纳秒 */1== 1000毫秒 == 1000 000微妙 == 1000 000 000纳秒
};

2、文件的设备号

struct stat
{dev_t     st_dev;    // 本文件所在的设备的设备号,适用于非设备文件...dev_t     st_rdev;   // 本文件的设备号,适用于特殊设备文件......};

Linux系统为了方便管理,为每一种设备分配了主次设备号,主设备号用来规范设备的类型;次设备号用来规范该种设备在本系统中的序号。设备号是系统资源,在设备被加载的时候分配完毕。

  • 以下两个函数,常用来获取主次设备号:
 unsigned int major(dev_t dev);  // 从 dev 中获取主设备号unsigned int minor(dev_t dev);  // 从 dev 中获取次设备号
  • 注意,在结构体stat中有两个类型为 dev_t 的成员。他们分别代表:
    • 本文件所在设备的设备号,适用于非设备文件。
    • 本文件的设备号,适用于特殊设备文件。
    • 特殊设备文件只有设备号的属性,没有文件大小的属性,即st_size是无效的。
  • 解析:
    • 非设备文件是没有设备号的,此时该文件的 st_rdev 成员是无效的。
    • 非设备文件一定是存储某个存储器上的,此时该文件的 st_dev 代表的是其所在存储器的设备号,比如某个硬盘。
    • 特殊设备文件指的是类型为字符设备或块设备的文件,比如键盘、鼠标、硬盘、显示器等。
    • 特殊设备文件的 st_dev 成员是无效的,只有 st_rdev 有效。

示例代码:

int main(int argc, char **argv)
{// 获取指定文件的属性信息struct stat info;stat(argv[1], &info); // 是特定的设备文件,那么 st_rdev 有效且 st_size 无效if(S_ISCHR(info.st_mode) || S_ISBLK(info.st_mode)){printf("该文件的主次设备号分别是:%d,%d\n",major(info.st_rdev),minor(info.st_rdev));}// 不是设备文件,那么 st_dev 有效且 st_size 也有效else{printf("文件大小是:%d", info.st_size);printf("文件所在设备的主次设备号分别是:%d,%d\n",major(info.st_dev),minor(info.st_dev));}
}

3、类型与权限

struct stat
{mode_t    st_mode;   // 文件类型 + 文件权限......};

在结构体 stat 中,文件的类型和权限并没有分开存储,而是被统一存储到同一个成员 st_mode 中,该成员的内部结构如下所示:
在这里插入图片描述

  • 关键点:
    • st_mode是一个16位的 short 短整型数据。
    • 前4位表达文件的类型,由于Linux文件类型总共7种。(0000到1111)
    • 中间三位分别是 setuid、setgid 和 stickyBit。
    • 后9位表达文件的权限,与三组权限一一对应。
  • 判断文件的类型可以用如下宏即可:
S_ISREG(st_mode)  is it a regular file?                     // 是一个普通文件吗?
S_ISDIR(st_mode)  directory?                                // 是一个目录文件吗?
S_ISCHR(st_mode)  character device?                         // 是一个字符设备文件吗?
S_ISBLK(st_mode)  block device?                             // 是一个块设备文件吗?
S_ISFIFO(st_mode) FIFO (named pipe)?                        // 是一个管道文件吗?(系统编程)
S_ISLNK(st_mode)  symbolic link?  (Not in POSIX.1-1996.)    // 是一个链接文件吗?(快捷方式)
S_ISSOCK(st_mode) socket?  (Not in POSIX.1-1996.)           // 是一个网络文件吗?(网络编程)
  • setuid、setgid(只对普通文件有效)与stickyBit (只针对目录有效)
  • 作用解析:
    • setuid: 使权得文件的使用者获得文件所有者的临时授。 // ubuntu20.04系统没用
    • setgid: 使得文件的使用者获得文件所在目录的所属组的临时授权。
    • stickyBit:使得用户只能增加和删除属于自身的文件,不能删除别的用户的文件 // ubuntu20.04系统有用
  • 修改文件setuID示例:
ls -l
-rwxr-xr-x 1 xxx xxx  8520 Dec 18 00:27 a.out
chmod 04755 a.out
ls -l
-rwxr-xr-x 1 xxx xxx  8520 Dec 18 00:27 a.out

使用 setID 解决实际问题的一个典型例子,就是系统中用于修改密码的命令:

ls -l /usr/bin/passwd 
-rwsr-xr-x 1 root root 59640 Mar 23  2019 /usr/bin/passwd

程序命令 passwd 是属于根用户 root 的,但由于它被设置了setuid,因此以后不管是谁来执行这个程序,在其执行期间都会临时获得 root 的临时授权,这么做是因为修改密码的本质上修改文件 /etc/passwd 的内容,而该文件只有管理员 root 才能修改,设置了 setuid 之后,普通用户既可以通过命令 passwd 来修改此文件,也避免了索取管理员密码的步骤,非常实用。

  • 修改目录stickyBit示例:
ls -l
drwxrwxrwx  2 xxx xxx 4096 Dec 20 18:13 dir/
chmod 01777 dir/
ls -l
drwxrwxrwt  2 xxx xxx 4096 Dec 20 18:13 dir/

4、其他简单文件信息

  • 文件的所有者与所属组:
struct stat
{uid_t  st_uid;  // 文件所有者IDgid_t  st_gid;  // 文件所在组ID...
};

从结构体 stat 中获取的文件所有者和所属组信息中,只有它们的ID号,而没有切确的名称,可以通过以下函数来获取切确的名称:

struct passwd *getpwuid(uid_t uid);
struct group *getgrgid(gid_t gid);
  • 文件的尺寸相关信息:
struct stat
{nlink_t   st_nlink;   // 文件的别名的数目off_t     st_size;    // 文件大小blksize_t st_blksize; // 标准IO建议的数据块大小  blkcnt_t  st_blocks;  // 文件占用的数据块个数...
};
  • 关键点:
    a.文件别名数目也称为硬链接数目,亦即索引个数,文件系统正是以此来判断某个文件是否可以彻底删除的标记。
    b.文件大小就是执行命令 “ls -l” 时所展示大小,包含文件空间的大小。

二、目录操作

1、基本概念

\quad 目录也是一种文件,因此操作流程与普通文件类似,有诸如打开、关闭、定位等概念,但目录是一种特殊的文件,目录存储的数据的最小单位并不是字符,而是目录项。这使得目录跟普通文件又有区别。
\quad 在Linux文件系统的经典结构中,目录不同于文件夹,目录的本质是索引,文件夹的本质是容器。在Linux中,目录有几个要点:

  • 整个分区被分成两部分,一部分称为i节点域,另一部分称为数据域
    • i节点域记录的是整个分区的基本信息,包括分区可用空间和已用空间的管理信息
    • 数据域存储文件实际内容数据
  • 每一个文件(包括目录本身)拥有一个唯一的标识,称为i节点号,分区使用i节点号管理并索引所有的文件,注意i节点号是分区内部的信息,就像美国的公民ID是美国内部管理信息一样,在中国是无效的,i节点号不能跨分区,这也是为什么使用命令 ln 创建文件别名不能跨分区的原因。
  • 目录所存储的数据单元是目录项,目录项指的是结构体
    struct dirent{},其内部保存的是文件的名称、i节点号等基本信息,不包含文件具体内容。
  • 任何一个目录至少包含两个目录项:.和…
    • [.]代表当前目录,[…]代表上一级目录(任意一个文件夹内敲:ls -a即可看到)
    • 如果本目录就是根目录,那么[…]也代表本机目录
      在这里插入图片描述

2、相关的接口函数

(1) 打开目录

#include <dirent.h>DIR *opendir(const char *name);返回值:成功 返回DIR指针表示打开的那个目录失败 NULL参数:name --》目录的路径名

(2) 读取目录(重点)

struct dirent *readdir(DIR *dirp);返回值:成功返回存放当前目录中的文件信息struct dirent {ino_t          d_ino;       //节点编号off_t          d_off;       //文件偏移位置unsigned short d_reclen;    unsigned char  d_type;      //文件类型DT_BLK      块设备DT_CHR      字符设备DT_DIR      目录DT_FIFO     管道DT_LNK      软链接DT_REG      普通文件DT_SOCK     套接字DT_UNKNOWN  文件不认识char           d_name[256];  //文件名字};失败或者读取目录完毕,会返回NULL
示例代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>int main(int argc, char const *argv[])
{DIR * dir_ret= opendir("/home/lmr");struct dirent *dirent;if (dir_ret == NULL){perror("打开目录失败\n");return -1;}// 循环读取目录while (1){// 读取目录中的目录项(指的是目录中包含各种类型的文件)dirent = readdir(dir_ret);if (dirent == NULL)break;if (dirent->d_type == DT_REG)printf("读取的是普通文件,名字为:%s\n", dirent->d_name);if (dirent->d_type == DT_DIR)printf("读取的是子目录(子文件夹),名字为:%s\n", dirent->d_name);}closedir(dir_ret);return 0;
}

(3) 新建目录

int mkdir(const char *pathname, mode_t mode);返回值:成功 0   失败 -1参数:pathname --》目录的路径名mode --》权限0777  0666   表面上写的是0777,但是实际的权限计算公式是:   0777&(~umask)
示例代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>int main(int argc, char const *argv[])
{// 新建空白目录// mkdir("./new_dir", 0777);// 不允许嵌套新建目录--错误写法// mkdir("./new_dir/123/456/789", 0777);// 可以这样写--正确写法 mkdir("./new_dir", 0777);mkdir("./new_dir/123", 0777);mkdir("./new_dir/123/456", 0777);mkdir("./new_dir/123/456/789", 0777);return 0;
}

(4) 删除目录

在这里插入图片描述

示例代码:
int main(void)
{// 在家目录下创建一个空目录mkdir("/home/xxx/a", 0755);// 将空目录删除(以下两条语句等价)rmdir("/home/xxx/a");remove("/home/xxx/a");
}

(5) 关闭目录

在这里插入图片描述


http://www.ppmy.cn/ops/169921.html

相关文章

飞书只有阅读权限的文档下载,飞书文档下载没有权限的文件

wx搜索公zhong号&#xff1a;"狮心王"回复"飞书文档保存"下载chrome扩展文件 拿到扩展文件之后给chrome添加扩展

2、二分和贪心

一、二分 这里有个小技巧&#xff0c;你会发现&#xff0c;只要是求最大最小最多等等的贪心过程&#xff0c;我们就有3种方法&#xff1a;①二分②贪心算法③动态规划 我们先讲二分和贪心&#xff0c;动态规划比较麻烦&#xff0c;留到后期。 1、了解 2、模版 class Solution …

探索 Ollama:开源大语言模型平台的无限可能​

在人工智能的快速发展进程中&#xff0c;大语言模型扮演着至关重要的角色。Ollama 作为一个开源的大语言模型平台&#xff0c;正逐渐崭露头角&#xff0c;为广大开发者和爱好者带来了全新的体验。它允许用户在本地环境中轻松地运行、创建和共享大型语言模型&#xff0c;极大地降…

音视频系列——Websockets接口封装为Http接口

模型服务示例&#xff1a;实时语音转文本服务 本示例展示一个支持双协议&#xff08;WebSocket流式接口HTTP同步接口&#xff09;的语音转文本模型服务&#xff0c;并提供将WebSocket接口封装为HTTP接口的代码实现。 一、服务架构设计 #mermaid-svg-nw0dMZ4uKfS4vGZR {font-fa…

C++学习之路:从头搞懂配置VScode开发环境的逻辑与步骤

目录 编辑器与IDE基于vscode的C开发环境配置1. 下载vscode、浅尝编译。番外篇 2. 安装插件&#xff0c;赋能编程。3. 各种json文件的作用。c_cpp_properties.jsontask.jsonlaunch.json 总结&&彩蛋 编辑器与IDE 上一篇博客已经介绍过了C程序的一个编译流程&#xff0c;从…

trae和Spring Boot Java 项目 ruoyi框架

再不拥抱AI&#xff0c;开发生涯肯定受限&#xff0c;再不享受AI&#xff0c;白白浪费键盘。 1.下载安装Trae 国际版&#xff1a;Trae 国内版&#xff1a;Trae - AI 原生 IDE https://www.trae.com.cn/ 国际版本需要翻墙&#xff0c;建议直接国内版本-主要是豆包大模型和DS R1、…

《基于Spring Boot+Vue的智慧养老系统的设计与实现》开题报告

个人主页:@大数据蟒行探索者 一、研究背景及国内外研究现状 1.研究背景 根据1982年老龄问题世界大会联合国制定的标准,如果一个国家中超过65岁的老人占全国总人口的7%以上,或者超过60岁的老人占全国总人口的10%以上,那么这个国家将被定义为“老龄化社会”[1]。 随着国…

DisplayPort(DP)详解

一、DisplayPort的定义与核心特性 DisplayPort&#xff08;DP&#xff09; 是由 视频电子标准协会&#xff08;VESA&#xff09; 制定的 高性能数字音视频接口&#xff0c;专为高分辨率显示器和多屏应用设计。其核心特性包括&#xff1a; 高带宽&#xff1a;DisplayPort 2.0支…