Linux基础——文件和目录操作函数

 转自:https://blog.csdn.net/jiangcunyu1998/article/details/108199771

 

  1. 本文内容大部分出自对传智播客linux课程内容的总结和课堂笔记。
  2. 若有常见命令的详细介绍或linux系统的扩展学习的需要,可以点击此处下载PDF书籍(鸟哥私房菜-基础篇、Linux命令速查手册)。
  3. 涉及到函数用法,可查找man文档中的详细介绍linux 中文 man离线手册
  4. 本文原文可参考我在语雀平台的原版笔记点击此处阅读

1. 文件操作

1.1 stat函数

1.1.1 作用

  • 获取文件属性(从inode上获取)
  • e.g. stat aaa

image.png

1.1.2 头文件

  • #include <sys/types.h>
  • #include <sys/stat.h>
  • #include <unistd.h>

1.1.3 函数声明

  • int stat(const char *pathname, struct stat *buf);
  • int fstat(int fd, struct stat *buf);

1.1.4 返回值

  • 成功返回0,失败返回-1

1.1.5 文件属性(struct stat)

image.png

1.1.6 mode_t st_mode

image.png

  • 该变量占 2byte 共 16位

  • 掩码的使用:st_mode & 掩码

    • st_mode与某类文件属性信息掩码做按位与操作,然后与宏比较,即可得到相应的信息
      • 如果想获取文件属性信息,st_mode&掩码

        • e.g. st_mode & S_IFMT可以得到文件类型
      • 如果看文件是否有哪个权限,st_mode&掩码&权限的掩码,然后看是否大于0

        • e.g. st_mode & S_IRWXU & S_IRUSR 可以看文件所有者是否有读权限
  • 文件类型(12-15 bit)

    • 掩码: S_IFMT 0170000 过滤 st_mode中除文件类型以外的信息
    • S_IFSOCK 0140000 套接字
    • S_IFLNK 0120000 符号链接(软链接)
    • S_IFREG 0100000 普通文件
    • S_IFBLK 0060000 块设备
    • S_IFDIR 0040000 目录
    • S_IFCHR 0020000 字符设备
    • S_IFIFO 0010000 管道
  • 特殊权限位(9-11 bit)很少用

    • S_ISUID 0004000 设置用户ID
    • S_ISGID 0002000 设置组ID
    • S_ISVTX 0001000 黏住位

image.png

  • 文件所有者权限(6-8 bit)

    • 掩码: S_IRWXU 00700 过滤 st_mode中除文件所有者权限以外的信息
    • S_IRUSR 00400 读权限
    • S_IWUSR 00200 写权限
    • S_IXUSR 00100 执行权限
  • 所属组权限(3-5 bit)

    • 掩码: S_IRWXG 00070 过滤 st_mode中除所属组权限以外的信息
    • S_IRGRP 00040 读权限
    • S_IWGRP 00020 写权限
    • S_IXGRP 00010 执行权限
  • 其他人权限(0-2 bit)

    • 掩码: S_IRWXO 00007 过滤 st_mode中除其他人权限以外的信息
    • S_IROTH 00004 读权限
    • S_IWOTH 00002 写权限
    • S_IXOTH 00001 执行权限

1.1.7 特性

  • 穿透(跟踪)软链接,读到的是软链接映射到的文件的属性信息
  • 相当于用vim打开一个软链接,打开的是软链接指向的文件

1.1.8 练习:利用stat实现类似ls的功能

image.png

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <unistd.h>
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 #include <fcntl.h>
 7 #include <pwd.h>
 8 #include <grp.h>
 9 #include <time.h>
10 #include <string.h>
11 int main(int argc, char** argv)
12 {
13     char **fn = argv;
14     if(argc < 2)
15     {
16         printf("miss a parameter: ./a.out filename\n");
17         exit(1);
18     }
19     struct stat st;
20     int ret = stat(fn[1], &st);
21     char ftmo[11] = {0};
22     //获取文件类型和权限
23     switch(st.st_mode & S_IFMT)
24     {
25         case S_IFSOCK: ftmo[0] = 's'; break;
26         case S_IFLNK: ftmo[0] = 'l'; break;
27         case S_IFREG: ftmo[0] = '-'; break;
28         case S_IFBLK: ftmo[0] = 'b'; break;
29         case S_IFDIR: ftmo[0] = 'd'; break;
30         case S_IFCHR: ftmo[0] = 'c'; break;
31         case S_IFIFO: ftmo[0] = 'p'; break;
32     }
33     //获取所有者权限
34     ftmo[1] = (st.st_mode & S_IRWXU & S_IRUSR)?'r':'-';
35     ftmo[2] = (st.st_mode & S_IRWXU & S_IWUSR)?'w':'-';
36     ftmo[3] = (st.st_mode & S_IRWXU & S_IXUSR)?'x':'-';
37     //获取组内权限
38     ftmo[4] = (st.st_mode & S_IRWXG & S_IRGRP)?'r':'-';
39     ftmo[5] = (st.st_mode & S_IRWXG & S_IWGRP)?'w':'-';
40     ftmo[6] = (st.st_mode & S_IRWXG & S_IXGRP)?'x':'-';
41     //获取其他人权限
42     ftmo[7] = (st.st_mode & S_IRWXO & S_IROTH)?'r':'-';
43     ftmo[8] = (st.st_mode & S_IRWXO & S_IWOTH)?'w':'-';
44     ftmo[9] = (st.st_mode & S_IRWXO & S_IXOTH)?'x':'-'; 
45     ftmo[10] = '.';
46     //硬链接数目
47     int linknum = st.st_nlink;
48     //文件所有者
49     char* uname = getpwuid(st.st_uid)->pw_name; 
50     //文件所属组
51     char* gname = getgrgid(st.st_gid)->gr_name;
52     //文件大小
53     size_t fsize = st.st_size;
54     //文件日期
55         //注意:ctime返回的字符串末尾有换行符,需要处理
56     char* ftime = ctime(&st.st_ctime);
57     ftime[strlen(ftime)-1] = 0;
58     //文件名
59     char* fname = fn[1];
60     //格式化字符串
61     char buf[100] = {0};
62     sprintf(buf, "%s %d %s %s %d %s %s", 
63             ftmo, linknum, uname, gname, fsize, ftime, fname);
64     printf("%s\n", buf);
65     return 0;
66 }

 

 

1.2 lstat函数

1.2.1 作用和头文件

  • 同stat

1.2.2 函数声明

  • int lstat(const char *pathname, struct stat *buf);

1.2.3 特性

  • 不穿透(跟踪) 软链接,读到的是软链接的属性信息
  • 相当于ls -l 和 rm,操作的是软链接的信息

1.3 access函数

1.3.1 作用

  • 测试指定文件是否拥有某种权限

1.3.2 头文件

  • #include <unistd.h>

1.3.3 原型

  • int access(const char *pathname, int mode);

1.3.4 参数

  • pathname --> 文件名

  • mode --> 权限类别

    • R_OK 是否有读权限
    • W_OK 是否有写权限
    • X_OK 是否有执行权限
    • F_OK 测试一个文件是否存在

1.3.5 返回值

  • 0 --> 所有欲查核的权限都通过了检查
  • -1 --> 有权限被禁止

1.4 chmod函数

1.4.1 作用

  • 改变文件的权限

1.4.2 头文件

  • #include <sys/stat.h>

1.4.3 原型

  • int chmod( const char *filename, int pmode );

1.4.4 参数

  • filename --> 文件名

  • pmode --> 权限

    • 必须是一个8进制数

    • 注:可以使用strtol函数将字符串转换为指定进制数 ‘0666’ -> 0666

      • #include <stdlib.h>
        -long int strtol(const char *nptr, char **endptr, int base); 其中endptr用于测试,可设为0

1.4.5 返回值

  • 0 --> 改变成功
  • -1 --> 失败

1.5 chown函数

1.5.1 作用

  • 改变文件的所有者

1.5.2 头文件

  • #include <unistd.h>

1.5.3 原型

  • int chown(const char *path, uid_t owner, gid_t group);

1.5.4 参数

  • 其中uid 和 gid可以通过 /etc/passwd 获得

  • passwd文件应该怎么看

    • man 5 passwd

1.5.5 返回值

  • 0 --> 成功
  • -1 --> 失败

1.6 truncate函数

1.6.1 作用

  • 将参数path 指定的文件大小改为参数length 指定的大小。如果原来的文件大小比参数length大,则超过的部分会被删去。

  • 扩展或截断一个文件

    • 假设文件长度为100
    • 第二个参数指定长度为20 ->文件被截断
    • 第二个参数指定长度为300 -> 文件被拓展(空洞文件)
  • 与lseek的主要区别是扩展时不需要写入内容

1.6.2 头文件

  • #include <unistd.h>
  • #include <sys/types.h>

1.6.3 原型

  • int truncate(const char *path, off_t length);
  • path --> 文件路径
  • length --> 指定的文件大小

1.6.4 返回值

  • 0 --> 执行成功
  • -1 --> 执行失败

1.7 链接

1.7.1 link函数

  • 作用:创建一个硬链接
  • 头文件:#include <unistd.h>
  • 原型:int link(const char *oldpath, const char newpath);*

1.7.2 symlink 函数

  • 作用:创建一个软连接
  • 其他同link

1.7.3 readlink 函数

  • **作用:**读软链接对应的文件名,不是读内容
  • 读非软链接会失败
    image.png

1.7.4 unlink 函数

  • 作用

    • 删除一个文件的目录项并减少它的链接数,若成功则返回0,否则返回-1,错误原因存于errno。
    • 如果想通过调用这个函数来成功删除文件,你就必须拥有这个文件的所属目录的写和执行权限。
  • 头文件#include <unistd.h>

  • 声明int unlink(const char *pathname);

  • 使用

    • 如果是软链接,删除软链接

    • 如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode

    • 如果文件硬链接数为0,但有进程已打开该文件,并持有文件描述符,则等该进程关闭该文件时,kernel才真正去删除该文件

      • 利用该特性创建临时文件,先open或create创建一个文件,马上unlink此文件
  • 示例:使用unlink函数制作一个可以自动删除的临时文件。比如看视频时的缓存文件,看完视频以后自动删除。
    image.png

1.8 rename 函数

  • 作用:文件重命名
  • 头文件stdio.h
  • 函数原型int rename(const char oldpath, const char newpath);

2. 目录操作

2.1 chdir 函数

  • 作用:修改当前进程的路径,相当于cd 修改当前工作目录,改变程序中打开的路径
  • 头文件#include <unistd.h>
  • 函数原型int chdir(const char *path);

2.2 getcwd 函数

  • 作用:获取当前进程的工作目录
  • 头文件#include <unistd.h>
  • 函数原型char *getcwd(char *buf, size_t size);
  • 示例:在当前目录中运行程序,然后在进程中切换目录到上级目录并创建一个chdir.txt文件
    image.png
    image.png

2.3 mkdir 函数

  • 作用:创建目录
  • 头文件#include <sys/stat.h> #include <sys/types.h>
  • 函数原型int mkdir(const char *pathname, mode_t mode);
  • 注意:创建的目录需要有执行权限,否则无法进入目录

2.4 rmdir 函数

  • 作用:删除一个空目录
  • 头文件#include <unistd.h>
  • 函数原型int rmdir(const char *pathname);

2.5 opendir 函数

  • 作用:打开一个目录
  • 头文件#include <sys/types.h> #include <dirent.h>
  • 函数原型DIR *opendir(const char *name);
  • 返回值
    • 成功返回:DIR结构指针,该结构是一个内部结构,保存所打开的目录信息,作用类似于FILE结构
    • 出错返回:NULL

2.6 readdir 函数

  • 作用:读目录
  • 头文件#include <dirent.h>
  • 函数原型struct dirent *readdir(DIR *dirp);
  • 返回值
    image.png
    • 其中d_type

      • DT_BLK - 块设备
      • DT_CHR - 字符设备
      • DT_DIR - 目录
      • DT_LNK - 软连接
      • DT_FIFO - 管道
      • DT_REG - 普通文件
      • DT_SOCK - 套接字
      • DT_UNKNOWN - 未知
    • -D_BSD_SOURCE 编译时添加宏定义

2.7 closedir 函数

  • 作用:关闭目录
  • 头文件#include <sys/types.h> #include <dirent.h>
  • 声明int closedir(DIR *dirp);
  • 示例:使用opendir\readdir\closedir递归获取指定目录下的普通文件个数
 1 #include <string.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <dirent.h>
 5 
 6 int getFileNum(char* root)
 7 {
 8     // 打开指定目录
 9     DIR* dir = opendir(root);
10     if(dir == NULL)
11     {
12         perror("opendir");
13         exit(0);
14     }
15 
16     // 递归读取当前目录的普通文件数目    
17     int total = 0;
18     char path[1024] = {0};
19     struct dirent* ptr = NULL;
20     while((ptr = readdir(dir)) != NULL)
21     {
22         // 过滤掉. 和 ..目录
23         if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
24         {
25             continue;
26         }
27         // 如果当前读取到的是普通文件
28         if(ptr->d_type == DT_REG)
29         {
30             total ++;
31         }
32         // 如果当前读取到的是目录
33         if(ptr->d_type == DT_DIR)
34         {
35             // 把目录名存储下来,递归调用读取
36             sprintf(path, "%s/%s", root, ptr->d_name);
37             total += getFileNum(path);
38         }
39     }
40     closedir(dir);
41     return total;
42 }
43 
44 int main(int argc, char* argv[])
45 {
46     // 获取指定目录下的普通文件数量
47     int total = getFileNum(argv[1]);
48     // 打印输出
49     printf("%s has file number: %d\n", argv[1], total);
50     return 0;
51 }

 

 

 

 

3. fcntl 函数

  • 作用:根据文件描述符来操作文件的状态

  • 头文件#include <fcntl.h>

  • 函数原型

    • int fcntl(int fd, int cmd);
    • int fcntl(int fd, int cmd, long arg);
    • int fcntl(int fd, int cmd, struct flock *lock);
  • 具体功能

    • 复制一个现有的描述符 – cmd

      • F_DUPFD
    • 获得/设置文件描述符标记 – cmd

      • F_GETFD
      • F_SETFD
    • 获得/设置文件状态标记 – cmd

      • F_GETFL——获取文件状态标记时,arg=0

        • 只读打开 O_RDONLY
        • 只写打开 O_WRONLY
        • 读写打开 O_RDWR
        • 执行打开 O_EXEC
        • 搜索打开目录 O_SEARCH
        • 追加写 O_APPEND
        • 非阻塞模式 O_NONBLOCK
      • F_SETFL——设置文件状态标记时,arg为arg|其他标识

        • 可更改的几个标识
        • O_APPEND
        • O_NONBLOCK
    • 获得/设置异步I/O所有权 – cmd

      • F_GETOWN
      • F_SETOWN
    • 获得/设置记录锁 – cmd

      • F_GETLK
      • F_SETLK
      • F_SETLKW
  • 示例:改变已经打开的文件的属性

    • 在文件打开过程中修改读写权限,如果不使用fcntl的话,必须先关闭文件,然后以新的权限打开文件。
    • 打开文件的时候权限为只读;修改文件的只读权限为追加 O_APPEND,指针会自动移到尾部,在文件尾部添加
      image.png

4. dup和dup2函数

  • 作用:复制现有的文件描述符 —— 重定向文件描述符

  • 头文件#include <unistd.h>

  • 声明

    • int dup(int oldfd);
    • int dup2(int oldfd, int newfd);
  • 使用

    • dup

      • 返回的是文件描述符表中没有占用的最小的文件描述符
    • dup2

      • old 和new不是同一个文件描述符,如果new是一个被打开的文件描述符,在拷贝前先关闭new
      • old和new是同一个文件描述符,不会关掉new,直接返回old

5. 解决gcc编译过程中c99语法报错的问题

  • ~/.bashrc
  • alias gcc='gcc -std=gnu99'

6. 索引节点inode

保存的其实是实际的数据的一些信息,这些信息称为“元数据”(也就是对文件属性的描述)。

例如:文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性,文件读取或修改的时间戳,链接数量,指向存储该内容的磁盘区块的指针,文件分类等等。

  • 注意数据分成: 元数据+数据本身
  • 注意inode怎样生成的: 每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定(现代OS可以动态变化),一般每2KB就设置一个inode。一般文件系统中很少有文件小于2KB的,所以预定按照2KB分,一般inode是用不完的。所以inode在文件系统安装的时候会有一个默认数量,后期会根据实际的需要发生变化。
  • 注意inode号: inode号是唯一的,表示不同的文件。其实在Linux内部的时候,访问文件都是通过inode号来进行的,所谓文件名仅仅是给用户容易使用的。当我们打开一个文件的时候,首先,系统找到这个文件名对应的inode号;然后,通过inode号,得到inode信息,最后,由inode找到文件数据所在的block,现在可以处理文件数据了。
  • inode和文件的关系: 当创建一个文件的时候,就给文件分配了一个inode。一个inode只对应一个实际文件,一个文件也会只有一个inode。inodes最大数量就是文件的最大数量。
  • FILE* fp = open(“file”);
 
posted @ 2022-07-11 19:56  墨尔基阿德斯  阅读(697)  评论(0编辑  收藏  举报