Unix/Linux编程:文件树遍历-----nftw()

本文解析了nftw()函数在文件系统遍历中的作用,讲解了它如何增强ftw()的功能,以及如何通过FTW_FLAGS调整遍历行为。重点介绍了参数如nopenfd、chdir和FTW_MOUNT等在实际应用中的意义。
摘要由CSDN通过智能技术生成
  • nftw()函数是对执行类似功能的老函数 ftw()的加强。由于提供了更多功能,对符号链接的处理也更易于把握
  • GNU C 语言函数库也提供了派生自 BSD 分支的 fts API(fts_open()、fts_read()、fts_children()、fts_set()和 fts_close())。这些函数执行的任务类似于 ftw()和 nftw(),但在遍历树方面为应用程序提供了更大的灵活性。
NAME
       ftw, nftw - file tree walk

SYNOPSIS
       #include <ftw.h>

       int nftw(const char *dirpath,
               int (*fn) (const char *fpath, const struct stat *sb,
                          int typeflag, struct FTW *ftwbuf),
               int nopenfd, int flags);

       #include <ftw.h>

       int ftw(const char *dirpath,
               int (*fn) (const char *fpath, const struct stat *sb,
                          int typeflag),
               int nopenfd);


nftw()函数遍历由 dirpath 指定的目录树,并为目录树中的每个文件调用一次由程序员定义的 fn 函数。

  • 默认情况下,nftw()会针对给定的树执行未排序的前序遍历,即对各目录的处理要先于各目录下的文件和子目录
  • 当nftw()遍历目录树时,最多会为树的每一层打开一个文件描述符。
  • 参数nopenfd指定了nftw()可以使用文件描述符数量的最大值
    • 如果目录树深度超过这一最大值,那么nftw()会在做好记录的前提下,关闭并重新打开描述符,从而避免同时持有的描述符数目突破上限nopenfd(从而导致运行越来越慢)
    • 在较老的 UNIX 实现中,有的系统要求每个进程可打开的文件描述符数量不得超过 20 个,这更突显出这一参数的必要性。现代 UNIX 实现允许进程打开大量的文件描述符,因此,在指定该数目时出手可以大方一些(比如,10 或者更多)。
  • nftw()的 flags 参数由 0 个或多个下列常量相或(|)组成,这些常量可对函数的操作做出修正
    • FTW_CHDIR
      • 在处理目录内容之前先调用 chdir()进入每个目录。
      • 如果打算让 func 在 pathname 参数所指定文件的驻留目录下展开某些工作,那么就应当使用这一标志。
    • FTW_DEPTH
      • 对目录树执行后序遍历。这意味着,nftw()会在对目录本身执行 func 之前先对目录中的所
        有文件(及子目录)执行 func 调用。
      • (这一标志名称容易引起误会—nftw()遍历目录树遵循的是深度优先原则,而非广度优先。而这一标志的作用其实就是将先序遍历改为后序遍历。
    • FTW_MOUNT
      • 不会越界进入另一文件系统。
      • 因此,如果树中某一子目录是挂载点,那么不会对其进行遍历。
    • FTW_PHYS
      • 默认情况下,nftw()对符号链接进行解引用操作。而使用该标志则告知 nftw()函数不要这么做。相反,函数会将符号链接传递给 func 函数,并将 typeflag 值置为 FTW_SL。

nftw()为每个文件调用 func 时传递 4 个参数。

  • 第一个参数 pathname 是文件的路径名。这个路径名可以是绝对路径,也可以是相对路径。
    • 如果指定 dirpath 时使用的是绝对路径,那么pathname 就可能是绝对路径。
    • 反之,如果指定 dirpath 时使用的是相对路径名,则 pathname中的路径可能是相对于进程调用 ntfw()时的当前工作目录而言。
  • 第二个参数 statbuf 是一枚指针,指向 stat 结构,内含该文件的相关信息。
  • 第三个参数 typeflag 提供了有关该文件的深入信息,并具有如下特征值之一
    • FTW_D :这是一个目录。
    • FTW_DNR :这是一个不能读取的目录(所以 nftw()不能遍历其后代)。
    • FTW_DP :正在对一个目录进行后序遍历,当前项是一个目录,其所包含的文件和子目录已经处理完毕。
    • FTW_F :该文件的类型是除目录和符号链接以外的任何类型。
    • FTW_NS :对该文件调用 stat()失败,可能是因为权限限制。Statbuf 中的值未定义。
    • FTW_SL :这是一个符号链接。仅当使用
    • FTW_PHYS :标志调用 nftw()函数时才返回该值。
    • FTW_SLN :这是一个悬空的符号链接。仅当未在flags参数中指定FTW_PHYS标志时才会出现该值
  • Func 的第四个参数 ftwbuf 是一枚指针,所指向结构定义如下:
    • 该结构的 base 字段是指 func 函数中 pathname 参数内文件名部分(最后一个“/”字符之后的部分)的整型偏移量
    • level 字段是指该条目相对于遍历起点(其 level 为 0)的深度
struct FTW{
	int base;
	int level;
};

每次调用 func 都必须返回一个整型值,由 nftw()加以解释。

  • 如果返回 0,nftw()会继续对树进行遍历,如果所有对 func 的调用均返回 0,那么 nftw()本身也将返回 0 给调用者。
  • 若返回非 0 值,则通知 nftw()立即停止对树的遍历,这时nftw()也会返回相同的非 0 值

由于 nftw()使用的数据结构是动态分配的,故而应用程序提前终止目录树遍历的唯一方法就是让 func 调用返回一个非 0 值

#if defined(__sun)
#define _XOPEN_SOURCE 500       /* Solaris 8 needs it this way */
#else
#if ! defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600
#define _XOPEN_SOURCE 600       /* Get nftw() and S_IFSOCK declarations */
#endif
#endif
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

static void
usageError(const char *progName, const char *msg)
{
    if (msg != NULL)
        fprintf(stderr, "%s\n", msg);
    fprintf(stderr, "Usage: %s [-d] [-m] [-p] [directory-path]\n", progName);
    fprintf(stderr, "\t-d Use FTW_DEPTH flag\n");
    fprintf(stderr, "\t-m Use FTW_MOUNT flag\n");
    fprintf(stderr, "\t-p Use FTW_PHYS flag\n");
    exit(EXIT_FAILURE);
}
static int                      /* Function called by nftw() */
dirTree(const char *pathname, const struct stat *sbuf, int type,
        struct FTW *ftwb)
{
    if (type == FTW_NS) {                  /* Could not stat() file */
        printf("?");
    } else {
        switch (sbuf->st_mode & S_IFMT) {  /* Print file type */
            case S_IFREG:  printf("-"); break;
            case S_IFDIR:  printf("d"); break;
            case S_IFCHR:  printf("c"); break;
            case S_IFBLK:  printf("b"); break;
         //   case S_IFLNK:  printf("l"); break;
            case S_IFIFO:  printf("p"); break;
           // case S_IFSOCK: printf("s"); break;
            default:       printf("?"); break; /* Should never happen (on Linux) */
        }
    }

    printf(" %s  ", (type == FTW_D)  ? "D  " : (type == FTW_DNR) ? "DNR" :
                                               (type == FTW_DP) ? "DP " : (type == FTW_F)   ? "F  " :
                                                                          (type == FTW_SL) ? "SL " : (type == FTW_SLN) ? "SLN" :
                                                                                                     (type == FTW_NS) ? "NS " : "  ");

    if (type != FTW_NS)
        printf("%7ld ", (long) sbuf->st_ino);
    else
        printf("        ");

    printf(" %*s", 4 * ftwb->level, "");        /* Indent suitably */
    printf("%s\n",  &pathname[ftwb->base]);     /* Print basename */
    return 0;                                   /* Tell nftw() to continue */
}
int
main(int argc, char *argv[])
{
    int flags, opt;

    flags = 0;
    while ((opt = getopt(argc, argv, "dmp")) != -1) {
        switch (opt) {
            case 'd': flags |= FTW_DEPTH;   break;
            case 'm': flags |= FTW_MOUNT;   break;
            case 'p': flags |= FTW_PHYS;    break;
            default:  usageError(argv[0], NULL);
        }
    }

    if (argc > optind + 1)
        usageError(argv[0], NULL);

    if (nftw((argc > optind) ? argv[optind] : ".", dirTree, 10, flags) == -1) {
        perror("nftw");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值