Linux系统编程---遍历目录

1. 概述

在Linux C中可以通过以下3种方式进行目录的遍历

  • (1)使用readdir
  • (2)使用glob
  • (3)使用nftw

2. 使用readdir遍历

readdir实际上是一系列函数的集合,它们使用的是类似于读写文件风格的API,包括的主要函数有:

opendir
readdir
seekdir
telldir
rewinddir
closedir

读写的过程如下:

  1. 调用opendir打开一个目录(参数是路径)
  2. 将errno置为空(因为readdir在错误和正常结束时都返回NULL,无法通过返回值确定是哪种情况,需要借助于errno)
  3. 在循环中调用readdir遍历整个目录(注意:readdir只遍历一层目录,如果想遍历整个目录树,需要自己使用递归进行)
  4. 退出循环后使用close关闭

基本的一个使用示例如下:

#include 
#include 
#include 
#include 
#include 

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "usage: %s \n", argv[0]);
        exit(1);
    }

    DIR *dir = opendir(argv[1]);
    if (dir == NULL) {
        perror("opendir()");
        exit(1);
    }

    errno = 0;

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || 
            strcmp(entry->d_name, "..") == 0)
            continue;
        printf("%s\n", entry->d_name);
    }
    closedir(dir);

    exit(0);
}

3. 使用glob遍历

glob的API有点类似于解析main函数中参数argc和argv的风格,并且glob的遍历中提供了对于目录中文件名的通配符匹配,函数原型如下:

#include 
int glob(
  const char *pattern, //文件名的通配符
  int flags, //对搜索结果进行过滤
  int (*errfunc) (const char *epath, int eerrno),//如果报错,那么会进行回调的函数
  glob_t *pglob //最终搜集结果的结构体
 );
//释放pglob的申请的空间
void globfree(glob_t *pglob);

基本的一个示例程序如下:

#include 
#include 
#include 
#include 

//由于glob调用第3个参数设置成了NULL,因此这个函数
//没有用到
int errfunc(const char *errpath, int err) {
    puts(errpath);
    fprintf(stderr, "glob error: %s\n", strerror(err));
}

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "usage: %s \n", argv[0]);
        exit(1);
    }

    glob_t globres;
    char buf[128];
    bzero(buf, 128);
    sprintf(buf, "%s/*", argv[1]);
    int err = glob(buf, 0, NULL, &globres);
    if (err) {
        printf("Error code = %d\n", err);
        exit(1);
    }

    for (int i = 0; i < globres.gl_pathc; i++) {
        printf("%s\n", globres.gl_pathv[i]);
    }

    globfree(&globres);
}

这个函数实现的效果和readdir略有差别,如果我们不设置flag,那么默认得到的文件集合中不包含隐藏文件

4. 使用nftw遍历

在遍历目录的时候使用nftw是更好的一种选择

  • 它可以处理在遍历的过程中可能出现的目录被移动、重命名和删除的情况(被其他进程进行改动)
  • nftw默认是进行整个目录的递归遍历
tips:在使用这个函数之前需要在#include之前先定义#define _XOPEN_SOURCE 500,或者是在编译选项中加入宏定义 gcc -D_XOPEN_SOURCE=500,又或者在Makefile中添加 -D_OXPEN_SOURCE=500,否则会报编译错误

这个nftw的大部分处理工作都是在回调函数内,回调函数提供了大量关于遍历到的文件和目录的信息(包括它们的名称,它们的stat,以及它们所处的层级),这些信息都是回调函数的参数传递给我们,我们只需要在回调函数内部使用这些参数即可

一个类似于上面程序的示例(默认nftw打印所有层级[递归]的方式,为了只输出一层需要额外做一些flag的设置)

#define _GNU_SOURCE
#define _XOPEN_SOURCE 700

#include 
#include 
#include 
#include 
#include 

//每一层级使用一个文件描述符,15层深的目录
//已经非常可观了
#define NUM_FDS 15
#define DEPTH_LIMIT 1

int print_entry(const char *filepath, const struct stat *info,
                const int typeflag, struct FTW *ftwbuf) {

    //跳过最外层的参数 argv[1]目录名的输出
    if (ftwbuf->level == 0)
        return FTW_CONTINUE;

    if (DEPTH_LIMIT == ftwbuf->level == DEPTH_LIMIT && FTW_D == typeflag) {
        printf("%s\n", filepath);
        return FTW_SKIP_SUBTREE; // don't get into the directory
    } else {
        printf("%s\n", filepath);
        return FTW_CONTINUE;
    }
}

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "usage: %s \n", argv[1]);
        exit(1);
    }

    int result =
        nftw(argv[1], print_entry, NUM_FDS, FTW_PHYS | FTW_ACTIONRETVAL);

    exit(0);
}

5. 参考资料

1.How to recursively list directories in C on Linux?

2.using nftw to only traverse the folder specified

你可能感兴趣的:(linux编程)