APUE-文件和目錄(六)函數ftw和nftw


名字

ftw,nftw - 文件樹遍歷

概要

#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()

nftw()遍歷位於文件夾dirpath下面的目錄樹,為每個樹的節點調用一次fn()。默認情況下,當前目錄總是先於其包含的文件和子目錄被處理(先序遍歷)。

為了避免調用進程的文件描述符被用盡,nopenfd指定了 nftw() 能夠同時打開目錄的最大數量。當搜索深度超過這個值,nftw() 將會變慢,因為目錄必須被關掉和重新打開。nftw() 為目錄樹中的每一層至多使用一個文件描述符。

對於在樹中發現的每個節點,nftw() 為其調用帶四個參數的函數fn(),這四個參數為fpathsbtypeflagftwbuffpath是節點的路徑名,它可以表示為相對路徑或者絕對路徑,相對路徑是相對調用進程的當前工作目錄。sb是為fpath調用stat函數所返回的指向stat結構體的指針。typeflag是一個整型值,取下面其中一個值:

  • FTW_F fpath是一個普通文件。
  • FTW_D fpath是一個目錄。
  • FTW_DNR fpath是一個不能被讀的目錄。
  • FTW_DP fpath是一個目錄,並且 flag參數被指定為FTW_DEPTH。(如果flags沒有被指定為FTW_DEPTH,那么訪問目錄時使用的typeflag總會是FTW_D。)路徑fpath下的所有文件和子目錄已經被處理過了。
  • FTW_NS 在不是符號鏈接的fpath上調用stat失敗。可能的原因是調用者對父目錄有讀權限,所以文件名fpath可以被看到,但是沒有執行權限,所以執行stat失敗。由sb指向的緩存的內容是未定義的。
  • FTW_SL fpath是一個符號鏈接,flags被設置為FTW_PHYS
  • FTW_SLN fpath是一個指向不存在的文件的符號鏈接。(只在FTW_PHYS未被設置的時候才會發生。)

當調用fn()nftw() 為其提供的第四個參數是一個類型為FTW的結構體:

struct FTW 
{
   int base;
   int level;
};

base是在fpath中給定的路徑名中的文件名(basename)的偏移量。levelfpath在目錄樹中相對於根節點的深度(dirpath的深度為0)。

為了讓樹的遍歷停止,fn() 返回一個非0值;這個值將會成為 nftw() 的返回值。只要fn()返回值為0,nftw()將會繼續遍歷目錄樹,直到要么遍歷完整個樹,在這種情況下會返回0;要么遇到一個錯誤(比如malloc失敗),在這種情況下返回-1。

因為nftw() 使用動態數據結構,遍歷目錄樹時唯一的安全退出方法就是從fn() 返回一個非0值。為了讓信號量終止遍歷時不會造成內存泄露,讓處理這設置一個全局的flag,由fn()對這個全局flag進行檢查。不要使用longjmp,除非程序將會終止(terminate)。

nftw()flags 參數由下面的一個或者多個的flags進行或運算所形成:

  • FTW_ACTIONRETVAL (從 glibc 2.3.3開始支持)
    如果這個特定的glibc的flag被設置,nftw() 會對從fn()返回的值進行不同處理。fn() 應該返回下面的值的其中一個:

    1. FTW_CONTINUE
      nftw() 繼續正常進行。

    2. FTW_SKIP_SIBLINGS
      如果fn() 返回這個值,當前節點的兄弟節點會被跳過,處理從父節點繼續進行。

    3. FTW_SKIP_SUBTREE
      如果一個目錄節點調用fn()(typeflag是FTW_D),這個返回值會阻止這個目錄下的對象作為參數傳遞給fn()nftw() 繼續處理當前目錄的下一個兄弟節點。

    4. FTW_STOP
      這會導致nftw() 立即返回FTW_STOP

      其它返回值可以關聯到未來的一些新的行為上;fn() 不應該返回除上面列出的值之外的其它值。

      為了從<ftw.h>中獲取FTW_ACTIONRETVAL的定義,必須在Include任何頭文件之前定義功能測試宏 _GNU_SOURCE。

  • FTW_CHDIR
    如果設置了這個flag,在處理每個目錄的內容之前,都會chdir(2)到這個目錄。如果程序需要在fpath所在的某個目錄做一些操作,這就是有用的。(指定這個flag不會對作為fn 參數fpath進行傳遞的路徑名有影響。)

  • FTW_DEPTH
    設置這個flag會進行后序遍歷,也就是在處理完當前目錄的內容和它的所有子目錄之后才會調用fn() (默認情況下,每個目錄在它的內容之前被處理。)

  • FTW_MOUNT
    設置這個flag,就會停留在同一個文件系統中(也就是不會跨越掛載點)。

  • FTW_PHYS

    設置這個flag,就不會跟隨符號鏈接。(這是你想做的。)如果不設置這個flag,就會跟隨符號鏈接,但是沒有文件會被報告兩次。

    如果FTW_PHYS沒有被設置,但是設置了FTW_DEPTH,那么函數fn() 就永遠不會被自己是自己子孫的目錄調用到。

ftw()

ftw() 是一個舊函數,它提供的功能是nftw()函數功能的一個子集。它們的區別如下:

  • ftw() 沒有flags參數。它和調用參數flags 為0的nftw()等價。
  • 回調函數fn() 中沒有提供第四個參數。
  • 提供給fn()typeflag 參數可供選擇的范圍更小:只有FTW_F,FTW_D, FTW_DNR, FTW_NS,(可能支持) FTW_SL.

返回值

這些函數成功返回0,失敗返回-1.

如果fn() 返回非0值,那么樹的遍歷被終止,fn() 的返回值被當作ftw()nftw() 的返回值返回。

如果nftw() 的調用設置了FTW_ACTIONRETVAL flag,那么應該被fn() 使用的用來終止樹的遍歷的唯一一個非0值就是FTW_STOP,這個值作為nftw() 的執行結果被返回。

例子

下面的程序遍歷以第一個命令行參數作為路徑名下的目錄樹,如果沒有指定路徑名參數,那么就遍歷當前目錄下的目錄樹。它會顯示每個文件的不同信息。第二個命令行參數用來指定一些字符,它們可以控制調用nftw() 時賦給flags 參數的值。

#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

static int
display_info(const char *fpath, const struct stat *sb,
             int tflag, struct FTW *ftwbuf)
{
    printf("%-3s %2d ",
            (tflag == FTW_D) ?   "d"   : (tflag == FTW_DNR) ? "dnr" :
            (tflag == FTW_DP) ?  "dp"  : (tflag == FTW_F) ?   "f" :
            (tflag == FTW_NS) ?  "ns"  : (tflag == FTW_SL) ?  "sl" :
            (tflag == FTW_SLN) ? "sln" : "???",
            ftwbuf->level);

    if (tflag == FTW_NS)
        printf("-------");
    else
        printf("%7jd", (intmax_t) sb->st_size);

    printf("   %-40s %d %s\n",
            fpath, ftwbuf->base, fpath + ftwbuf->base);

    return 0;           /* To tell nftw() to continue */
}

int
main(int argc, char *argv[])
{
    int flags = 0;

    if (argc > 2 && strchr(argv[2], 'd') != NULL)
        flags |= FTW_DEPTH;
    if (argc > 2 && strchr(argv[2], 'p') != NULL)
        flags |= FTW_PHYS;

    if (nftw((argc < 2) ? "." : argv[1], display_info, 20, flags)
            == -1) {
        perror("nftw");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM