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

文章目录

  • 1. 文件操作
    • 1.1 stat函数
      • 1.1.1 作用
      • 1.1.2 头文件
      • 1.1.3 函数声明
      • 1.1.4 返回值
      • 1.1.5 文件属性(struct stat)
      • 1.1.6 mode_t st_mode
      • 1.1.7 特性
      • 1.1.8 练习:利用stat实现类似ls的功能
    • 1.2 lstat函数
      • 1.2.1 作用和头文件
      • 1.2.2 函数声明
      • 1.2.3 特性
    • 1.3 access函数
      • 1.3.1 作用
      • 1.3.2 头文件
      • 1.3.3 原型
      • 1.3.4 参数
      • 1.3.5 返回值
    • 1.4 chmod函数
      • 1.4.1 作用
      • 1.4.2 头文件
      • 1.4.3 原型
      • 1.4.4 参数
      • 1.4.5 返回值
    • 1.5 chown函数
      • 1.5.1 作用
      • 1.5.2 头文件
      • 1.5.3 原型
      • 1.5.4 参数
      • 1.5.5 返回值
    • 1.6 truncate函数
      • 1.6.1 作用
      • 1.6.2 头文件
      • 1.6.3 原型
      • 1.6.4 返回值
    • 1.7 链接
      • 1.7.1 link函数
      • 1.7.2 symlink 函数
      • 1.7.3 readlink 函数
      • 1.7.4 unlink 函数
    • 1.8 rename 函数
  • 2. 目录操作
    • 2.1 chdir 函数
    • 2.2 getcwd 函数
    • 2.3 mkdir 函数
    • 2.4 rmdir 函数
    • 2.5 opendir 函数
    • 2.6 readdir 函数
    • 2.7 closedir 函数
  • 3. fcntl 函数
  • 4. dup和dup2函数
  • 5. 解决gcc编译过程中c99语法报错的问题
  • 6. 索引节点inode

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

1. 文件操作

1.1 stat函数

1.1.1 作用

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

Linux基础——文件和目录操作函数_第1张图片

1.1.2 头文件

  • #include
  • #include
  • #include

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)

Linux基础——文件和目录操作函数_第2张图片

1.1.6 mode_t st_mode

Linux基础——文件和目录操作函数_第3张图片

  • 该变量占 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 黏住位

Linux基础——文件和目录操作函数_第4张图片

  • 文件所有者权限(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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char** argv)
{
    char **fn = argv;
    if(argc < 2)
    {
        printf("miss a parameter: ./a.out filename\n");
        exit(1);
    }
    struct stat st;
    int ret = stat(fn[1], &st);
    char ftmo[11] = {0};
    //获取文件类型和权限
    switch(st.st_mode & S_IFMT)
    {
        case S_IFSOCK: ftmo[0] = 's'; break;
        case S_IFLNK: ftmo[0] = 'l'; break;
        case S_IFREG: ftmo[0] = '-'; break;
        case S_IFBLK: ftmo[0] = 'b'; break;
        case S_IFDIR: ftmo[0] = 'd'; break;
        case S_IFCHR: ftmo[0] = 'c'; break;
        case S_IFIFO: ftmo[0] = 'p'; break;
    }
    //获取所有者权限
    ftmo[1] = (st.st_mode & S_IRWXU & S_IRUSR)?'r':'-';
    ftmo[2] = (st.st_mode & S_IRWXU & S_IWUSR)?'w':'-';
    ftmo[3] = (st.st_mode & S_IRWXU & S_IXUSR)?'x':'-';
    //获取组内权限
    ftmo[4] = (st.st_mode & S_IRWXG & S_IRGRP)?'r':'-';
    ftmo[5] = (st.st_mode & S_IRWXG & S_IWGRP)?'w':'-';
    ftmo[6] = (st.st_mode & S_IRWXG & S_IXGRP)?'x':'-';
    //获取其他人权限
    ftmo[7] = (st.st_mode & S_IRWXO & S_IROTH)?'r':'-';
    ftmo[8] = (st.st_mode & S_IRWXO & S_IWOTH)?'w':'-';
    ftmo[9] = (st.st_mode & S_IRWXO & S_IXOTH)?'x':'-'; 
    ftmo[10] = '.';
    //硬链接数目
    int linknum = st.st_nlink;
    //文件所有者
    char* uname = getpwuid(st.st_uid)->pw_name; 
    //文件所属组
    char* gname = getgrgid(st.st_gid)->gr_name;
    //文件大小
    size_t fsize = st.st_size;
    //文件日期
        //注意:ctime返回的字符串末尾有换行符,需要处理
    char* ftime = ctime(&st.st_ctime);
    ftime[strlen(ftime)-1] = 0;
    //文件名
    char* fname = fn[1];
    //格式化字符串
    char buf[100] = {0};
    sprintf(buf, "%s %d %s %s %d %s %s", 
            ftmo, linknum, uname, gname, fsize, ftime, fname);
    printf("%s\n", buf);
    return 0;
}

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

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

1.4.3 原型

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

1.4.4 参数

  • filename --> 文件名

  • pmode --> 权限

    • 必须是一个8进制数

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

      • #include
        -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

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
  • #include

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
  • 原型: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

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

  • 使用

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

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

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

      • 利用该特性创建临时文件,先open或create创建一个文件,马上unlink此文件
  • 示例:使用unlink函数制作一个可以自动删除的临时文件。比如看视频时的缓存文件,看完视频以后自动删除。
    Linux基础——文件和目录操作函数_第5张图片

1.8 rename 函数

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

2. 目录操作

2.1 chdir 函数

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

2.2 getcwd 函数

  • 作用:获取当前进程的工作目录
  • 头文件#include
  • 函数原型char *getcwd(char *buf, size_t size);
  • 示例:在当前目录中运行程序,然后在进程中切换目录到上级目录并创建一个chdir.txt文件
    Linux基础——文件和目录操作函数_第6张图片
    Linux基础——文件和目录操作函数_第7张图片

2.3 mkdir 函数

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

2.4 rmdir 函数

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

2.5 opendir 函数

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

2.6 readdir 函数

  • 作用:读目录
  • 头文件#include
  • 函数原型struct dirent *readdir(DIR *dirp);
  • 返回值
    Linux基础——文件和目录操作函数_第8张图片
    • 其中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 #include
  • 声明int closedir(DIR *dirp);
  • 示例:使用opendir\readdir\closedir递归获取指定目录下的普通文件个数
#include 
#include 
#include 
#include 

int getFileNum(char* root)
{
    // 打开指定目录
    DIR* dir = opendir(root);
    if(dir == NULL)
    {
        perror("opendir");
        exit(0);
    }

    // 递归读取当前目录的普通文件数目    
    int total = 0;
    char path[1024] = {0};
    struct dirent* ptr = NULL;
    while((ptr = readdir(dir)) != NULL)
    {
        // 过滤掉. 和 ..目录
        if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
        {
            continue;
        }
        // 如果当前读取到的是普通文件
        if(ptr->d_type == DT_REG)
        {
            total ++;
        }
        // 如果当前读取到的是目录
        if(ptr->d_type == DT_DIR)
        {
            // 把目录名存储下来,递归调用读取
            sprintf(path, "%s/%s", root, ptr->d_name);
            total += getFileNum(path);
        }
    }
    closedir(dir);
    return total;
}

int main(int argc, char* argv[])
{
    // 获取指定目录下的普通文件数量
    int total = getFileNum(argv[1]);
    // 打印输出
    printf("%s has file number: %d\n", argv[1], total);
    return 0;
}

3. fcntl 函数

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

  • 头文件#include

  • 函数原型

    • 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,指针会自动移到尾部,在文件尾部添加
      Linux基础——文件和目录操作函数_第9张图片

4. dup和dup2函数

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

  • 头文件#include

  • 声明

    • 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”);

你可能感兴趣的:(#,Linux基础,linux)