亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

深入linux下遍歷目錄樹的方法總結分析

2020-01-26 16:02:09
字體:
來源:轉載
供稿:網友
前幾天需要實現對整個目錄樹的遍歷,查閱了相關的一些資料。開始找到的原始的方法是使用readdir()與lstat()函數實現遞歸遍歷,后來發現linux對于目錄遍歷這種最常用的操作已經提供了很完善的接口:ftw()與nftw()。下面就這兩種方法具體說明一下。
1、手動實現遞歸
1.1 stat()函數族
stat函數族包括:stat,fstat以及lstat函數,都是向用戶返回文件的屬性信息(元數據)。
復制代碼 代碼如下:

view plaincopy to clipboardprint?
#include <sys/stat.h>  
       int stat(const char*pathname,struct stat*buf);  
       int fstat(int filedes,struct stat*buf);  
       int lstat(const char *pathname,struct stat*buf); 
 #include <sys/stat.h>
        int stat(const char*pathname,struct stat*buf);
        int fstat(int filedes,struct stat*buf);
        int lstat(const char *pathname,struct stat*buf);

三個函數的返回:若成功為0,出錯為-1。對一個pathname,stat函數返回一個與此命名文件有關的信息結構,fstat函數獲得已在描述符filedes上打開的文件的有關信息。 lstat函數類似于stat,但是當命名的文件是一個符號連接時,lstat返回該符號連接的有關信息,而不是由該符號連接引用的文件的信息。
第二個參數是個指針,它指向一個我們應提供的結構。這些函數填寫由buf指向的結構。該結構的實際定義可能所實施而有所不同,但其基本形式是:
復制代碼 代碼如下:

      struct stat{
        mode st_mode; /*文件類型和方式(許可數)*/
        ino st_ino;/* i-節點號(序列號)*/
        dev st_dev;/*設備號(文件系統)*/
        dev st_rdev;/*特殊文件的設備號*/
        nlink st_nlink;/*連接數*/
        uid st_uid;/*屬主的用戶ID*/
        gid st_gid;/*屬主的組ID*/
        off st_size;/*普通文件的字節長度*/
        time st_atime;/*最后存取時間*/
        time st_mtime;/*最后修改存取時間*/
        time st_ctime;/*最后文件狀態更改時間*/
        long st_blksize;/*最佳I/O塊長*/
        long st_blocks;/*分配的512字節塊塊數
        };

下面是一個簡單的測試
復制代碼 代碼如下:

view plaincopy to clipboardprint?
#include<unistd.h>  
#include<sys/stat.h>  
#include<stdio.h>  
int 
main(int argc, char **argv){  
  struct stat buf;  
  if(stat(argv[1],&buf)) {  
    printf("[stat]:error!/n");  
    return -1;  
  }  
  printf("st_dev:%d/n",buf.st_dev);  
  printf("st_ino:%d/n",buf.st_ino);  
  printf("st_mode:%d S_ISDIR:%d/n",buf.st_mode,S_ISDIR(buf.st_mode));  
  printf("st_nlink:%d/n",buf.st_nlink);  
  printf("st_uid:%d/n",buf.st_uid);  
  printf("st_gid:%d/n",buf.st_gid);  
  printf("st_rdev:%d/n",buf.st_rdev);  
  printf("st_size:%d/n",buf.st_size);  
  printf("st_blksize:%lu/n",buf.st_blksize);  
  printf("st_blocks:%lu/n",buf.st_blocks);  
  printf("st_atime:%ld/n",buf.st_atime);  
  printf("st_mtime:%ld/n",buf.st_mtime);  
  printf("st_ctime:%ld/n",buf.st_ctime);  
  return 0;  

#include<unistd.h>
#include<sys/stat.h>
#include<stdio.h>
int
main(int argc, char **argv){
  struct stat buf;
  if(stat(argv[1],&buf)) {
    printf("[stat]:error!/n");
    return -1;
  }
  printf("st_dev:%d/n",buf.st_dev);
  printf("st_ino:%d/n",buf.st_ino);
  printf("st_mode:%d S_ISDIR:%d/n",buf.st_mode,S_ISDIR(buf.st_mode));
  printf("st_nlink:%d/n",buf.st_nlink);
  printf("st_uid:%d/n",buf.st_uid);
  printf("st_gid:%d/n",buf.st_gid);
  printf("st_rdev:%d/n",buf.st_rdev);
  printf("st_size:%d/n",buf.st_size);
  printf("st_blksize:%lu/n",buf.st_blksize);
  printf("st_blocks:%lu/n",buf.st_blocks);
  printf("st_atime:%ld/n",buf.st_atime);
  printf("st_mtime:%ld/n",buf.st_mtime);
  printf("st_ctime:%ld/n",buf.st_ctime);
  return 0;
}

這里補充說明一下linux中文件的基本類型。
1.普通文件(Regular file)。這是最常見的文件類型,這種文件包含了某種形式的數據。至于這種數據是文本還是二進制數據對于系統核而言并無區別。對普通文件內容的解釋由處理該文件的應用程序進行。
2.目錄文件(Directory file)。這種文件包含了其它文件的名字以及指向與這些文件有關信息的指針。對一個目錄文件具有讀許可數的任一進程都可以讀該目錄的內容,但只有系統核可以寫目錄文件。
3.字符特殊文件(Charocter special file)。這種文件用于系統中的某些類型的設備。
4.塊特殊文件(Block special file)。這種文件典型地用于磁盤設備。系統中的所有設備或者是字符特殊文件,或者是塊特殊文件。
5.FIFO。這種文件用于進程間的通信,有時也將其稱為命名管道。
6.套接口(socket)。這種文件用于進程間的網絡通信。套接口也可用于在一臺宿主機上的進程之間的非網絡通信。
7.符號連接(Symboliclink)。這種文件指向另一個文件。
對于文件類型,可以利用定義的宏比如S_ISDIR()等測試st_mode,判斷文件類型。宏有S_ISREG、S_ISDIR、S_ISCHR、S_ISBLK、S_ISFIFO、S_ISLNK、S_ISSOCK。
1.2 遍歷目錄例子
引用別人的一個例子,現在把許多文件處理函數集中在一起使用,程序遍歷指定目錄的文件,同時也要進到下級子目錄中進行遍歷,這一點是將子目錄遞歸傳遞到opendir中去,需要指出的是,這就決定了如果子目錄嵌套過深,程序將失敗返回,因為允許打開的子目錄流數量是有上限的。
復制代碼 代碼如下:

view plaincopy to clipboardprint?
/*  We start with the appropriate headers and then a function, printdir, 
    which prints out the current directory. 
    It will recurse for subdirectories, using the depth parameter is used for indentation.  */ 
#include <unistd.h>  
#include <stdio.h>  
#include <dirent.h>  
#include <string.h>  
#include <sys/stat.h>  
void printdir(char *dir, int depth)  
{  
    DIR *dp;  
    struct dirent *entry;  
    struct stat statbuf;  
    if((dp = opendir(dir)) == NULL) {  
        fprintf(stderr,"cannot open directory: %s/n", dir);  
        return;  
    }  
    chdir(dir);  
    while((entry = readdir(dp)) != NULL) {  
        lstat(entry->d_name,&statbuf);  
        if(S_ISDIR(statbuf.st_mode)) {  
            /**//* Found a directory, but ignore . and .. */ 
            if(strcmp(".",entry->d_name) == 0 ||   
                strcmp("..",entry->d_name) == 0)  
                continue;  
            printf("%*s%s//n",depth,"",entry->d_name);  
            /**//* Recurse at a new indent level */ 
            printdir(entry->d_name,depth+4);  
        }  
        else printf("%*s%s/n",depth,"",entry->d_name);  
    }  
    chdir("..");  
    closedir(dp);  
}  
/**//*  Now we move onto the main function.  */ 
int main(int argc, char* argv[])  
{  
    char *topdir, pwd[2]=".";  
    if (argc != 2)  
        topdir=pwd;  
    else 
        topdir=argv[1];  
    printf("Directory scan of %s/n",topdir);  
    printdir(topdir,0);  
    printf("done./n");  
    exit(0);  

/*  We start with the appropriate headers and then a function, printdir,
    which prints out the current directory.
    It will recurse for subdirectories, using the depth parameter is used for indentation.  */
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
void printdir(char *dir, int depth)
{
    DIR *dp;
    struct dirent *entry;
    struct stat statbuf;
    if((dp = opendir(dir)) == NULL) {
        fprintf(stderr,"cannot open directory: %s/n", dir);
        return;
    }
    chdir(dir);
    while((entry = readdir(dp)) != NULL) {
        lstat(entry->d_name,&statbuf);
        if(S_ISDIR(statbuf.st_mode)) {
            /**//* Found a directory, but ignore . and .. */
            if(strcmp(".",entry->d_name) == 0 ||
                strcmp("..",entry->d_name) == 0)
                continue;
            printf("%*s%s//n",depth,"",entry->d_name);
            /**//* Recurse at a new indent level */
            printdir(entry->d_name,depth+4);
        }
        else printf("%*s%s/n",depth,"",entry->d_name);
    }
    chdir("..");
    closedir(dp);
}
/**//*  Now we move onto the main function.  */
int main(int argc, char* argv[])
{
    char *topdir, pwd[2]=".";
    if (argc != 2)
        topdir=pwd;
    else
        topdir=argv[1];
    printf("Directory scan of %s/n",topdir);
    printdir(topdir,0);
    printf("done./n");
    exit(0);
}

2、使用ftw調用遍歷目錄
2.1ftw函數族
使用readdir函數等實現遞歸遍歷目錄樹的方法比較原始,glibc2.1收錄了ftw等函數,可以方便實現目錄樹的遍歷。
復制代碼 代碼如下:

view plaincopy to clipboardprint?
#include <ftw.h>  
int ftw(const char *dirpath,  
        int (*fn) (const char *fpath, const struct stat *sb,int typeflag),  
        int nopenfd);  
#define _XOPEN_SOURCE 500  
#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);
#define _XOPEN_SOURCE 500
#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);

具體的英文解釋可以參考文章《 ftw, nftw - file tree walk 》。
ftw()
函數說明:ftw() 會從參數dirpath指定的目錄開始,往下一層層地遞歸式遍歷子目錄。
ftw()會傳三個參數給fn(), 第一個參數*fpath指向當時所在的目錄路徑,第二個參數是*sb, 為stat結構指針,第三個參數為flag,有下面幾種可能值
FTW_F        一般文件
FTW_D       目錄
FTW_DNR    不可讀取的目錄,此目錄以下將不被遍歷
FTW_SL       符號連接
FTW_NS       無法取得stat結構數據,有可能是權限問題
最后一個參數depth代表ftw()在進行遍歷目錄時同時打開的文件數。ftw()在遍歷時每一層目錄至少需要一個文件描述詞,如果遍歷時用完了depth所給予的限制數目,整個遍歷將因不斷地關文件和開文件操作而顯得緩慢。(實際做測試的時候未發現...)

如果要結束ftw()的遍歷,fn()只需返回一非零值即可,此值同時也會是ftw()的返回值。否則ftw()會試著走完所有的目錄,然后返回0
返 回  值:遍歷中斷則返回fn()函數的返回值,全部遍歷則返回0,若有錯誤發生則返回-1
附加說明:由于ftw()會動態配置內存使用,請使用正常方式(fn函數返回非零值)來中斷遍歷,不要在fn函數中使用longjmp()
nftw()
函數說明:
nftw()與ftw()很像,都是從參數dirpath指定的目錄開始, 往下一層層地遞歸遍歷子目錄。 每進入一個目錄,便會調用參數*fn定義的函數來處理。nftw()會傳四個參數給fn(). 第一個參數*fpath指向當時所在的目錄路徑,第二個參數是*sb, 為stat結構指針(結構定義請參考stat()),第三個參數為typeflag,有底下幾種可能值:
FTW_F                         一般文件
FTW_D                         目錄
FTW_DNR                      不可讀取的目錄。此目錄以下將不被遍歷
FTW_SL                         符號連接
FTW_NS                        無法取得stat結構數據,在可能是權限問題
FTW_DP                        目錄,而且子目錄都已被遍歷過了
FTW_SLN                       符號連接,但連接不存在的文件
fn()的第四個參數是FTW結構,定義如下:
復制代碼 代碼如下:

struct  FTW
{
     int  base;
     int  level; //level代表遍歷時的深度
}

nftw()第三個參數depth代表nftw()在進行遍歷目錄時可同時打開的文件數。
ftw()在遍歷時每一層目錄至少需要一個文件描述詞,如果遍歷時用完了depth所給予的限制數目,整個遍歷將因不斷地關文件和開文件操作而顯得的緩慢
nftw()最后一個參數flags用來指定遍歷時的動作,可指定下列的操作或用OR組合
FTW_CHDIR                 在讀目錄之前先用chdir()移到此目錄
FTW_DEPTH                執行深度優先搜索。在遍歷此目錄前先將所有子目錄遍歷完
FTW_MOUNT               遍歷時不要跨越到其他文件系統
FTW_PHYS                  不要遍歷符號連接的目錄。預設會遍歷符號連接目錄
如果要結束nftw()的遍歷,fn()只需返回一非0值即可,此值同時也會是nftw()的返回值。否則nftw()會試著遍歷完所有目錄,然后返回0.
返 回 值 :遍歷中斷則返回fn()函數的返回值, 全部遍歷完則返回0,若有錯誤發生則返回-1
區別:ftw 對于每一個文件他都會調用stat函數,這就造成程序會跟隨符號鏈接。這就可能導致在某些情況下重復某些目錄或者循環統計某些目錄文件(這是因為符號鏈接的原因,詳細參見UNIX環境高級編程)。
nftw將調用lstat函數所以不存在跟隨符號鏈接的問題。
注意:使用nftw函數時,必須定義#define _XOPEN_SOURCE 500,否則會出現未定義等錯誤。
有一個沒搞清楚的問題是我使用FTW_DEPTH 來遍歷整個目錄樹的時候,遍歷到proc目錄下存在異常返回,可能還需要指定FTW_PHYS使其不遍歷符號鏈接目錄,這個有空查一下。
2、遍歷的例子
自己寫的一個測試的小例子。遍歷指定目錄,輸出文件元數據和遍歷深度等信息。
復制代碼 代碼如下:

view plaincopy to clipboardprint?
#define _XOPEN_SOURCE 500   
#include<ftw.h>  
#include<sys/stat.h>  
#include<unistd.h>  
#include<stdio.h>  
#include<string.h>   
#define FILEOPEN 1024   
int gb_filecount;  
int getMetadata(const char *dirpath, const struct stat *sb,int typeflag, struct FTW *ftwbuf);  
int main(int argc, char ** argv){  

  int ret = 0;  
  struct stat pathbuf;  
  if(argc > 2){  
    printf("-nfwt_t:invalid arguments /n ");  
    return -1;  
  }  
  if(stat(argv[1],&pathbuf)){  
    printf("-nfwt_t:invalid dirpath:%s/n",argv[1]);  
    return -1;  
  }else{  
    if(0 == S_ISDIR(pathbuf.st_mode)){  
      printf("-nfwt_t:/"%s/" is not dirpath/n",argv[1]);  
      return -1;  
    }  
  }  
  gb_filecount=0;  
  ret = nftw(argv[1],getMetadata,FILEOPEN,FTW_PHYS);  
    if(ret<0){  
    printf("-nftw:[wrong:%d]ntfw search %d files/n",ret,gb_filecount);  
    return -1;  
  }else{  
    printf("-nftw:[done:%d]trasvers in %s search %d files/n",ret,argv[1],gb_filecount);  
    return 0;  
  }  
}  
int   
getMetadata(const char *dirpath, const struct stat *sb,int typeflag, struct FTW *ftwbuf){  
  printf("num:%d path:%s ",++gb_filecount,dirpath);  
  printf("st_dev:%d ",(*sb).st_dev);  
  printf("st_ino:%d ",(*sb).st_ino);  
  printf("st_mode:%d S_ISDIR:%d ",(*sb).st_mode,S_ISDIR((*sb).st_mode));  
  printf("st_nlink:%d ",(*sb).st_nlink);  
  printf("st_uid:%d ",(*sb).st_uid);  
  printf("st_gid:%d ",(*sb).st_gid);  
  printf("st_rdev:%d ",(*sb).st_rdev);  
  printf("st_size:%d ",(*sb).st_size);  
  printf("st_blksize:%lu ",(*sb).st_blksize);  
  printf("st_blocks:%lu ",(*sb).st_blocks);  
  printf("st_atime:%ld ",(*sb).st_atime);  
  printf("st_mtime:%ld ",(*sb).st_mtime);  
  printf("st_ctime:%ld ",(*sb).st_ctime);  
  printf("typeflag:%d ",typeflag);  
  printf("FTW_base:%d FTW_level:%d /n",(*ftwbuf).base,(*ftwbuf).level);  
  return 0;  


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美高清视频在线观看| 狠狠躁天天躁日日躁欧美| 国色天香2019中文字幕在线观看| 久久国产精彩视频| 97国产在线观看| 久久久久久久久久国产| …久久精品99久久香蕉国产| 国产美女精品免费电影| 国产欧美一区二区| 亚洲直播在线一区| 亚洲欧美日韩精品久久奇米色影视| 欧美日本国产在线| 美日韩精品免费视频| 亚洲国产精品国自产拍av秋霞| 亚洲精品中文字幕有码专区| 免费99精品国产自在在线| 最近2019年日本中文免费字幕| 精品国内亚洲在观看18黄| 亚洲石原莉奈一区二区在线观看| 日韩免费在线免费观看| 日韩国产激情在线| 日本高清不卡的在线| 亚洲成人三级在线| 欧美影院在线播放| 欧美精品videossex88| 亚洲另类xxxx| 久久人人爽人人爽人人片av高请| 欧美激情精品久久久久久黑人| 成人免费午夜电影| 国产成人一区二区三区电影| 欧美美最猛性xxxxxx| 精品久久久久久久久久国产| 亚洲成人精品视频在线观看| 欧美日韩aaaa| 色狠狠久久aa北条麻妃| 欧洲午夜精品久久久| 在线播放精品一区二区三区| 欧美三级免费观看| 久久久久久成人精品| 亚洲成人激情图| 国产一区二区视频在线观看| 欧美黑人一级爽快片淫片高清| 欧美日韩国产中文精品字幕自在自线| 97人洗澡人人免费公开视频碰碰碰| 亚洲自拍小视频免费观看| 亚洲自拍偷拍网址| 久久精品99无色码中文字幕| 成人激情免费在线| 日韩成人av网址| 欧美孕妇性xx| 成人av番号网| 日韩免费视频在线观看| 26uuu另类亚洲欧美日本老年| 亚洲视频网站在线观看| 亚洲欧美国产高清va在线播| 91热精品视频| 久久99久久99精品中文字幕| 欧美黑人极品猛少妇色xxxxx| 成人欧美在线视频| 国产综合福利在线| 国产97在线|日韩| 国产亚洲欧美日韩精品| 亚洲欧美成人一区二区在线电影| 国产日韩综合一区二区性色av| 久久综合免费视频| 北条麻妃99精品青青久久| 91av免费观看91av精品在线| 国产香蕉一区二区三区在线视频| 欧美洲成人男女午夜视频| 亚洲精品狠狠操| 欧美美女15p| 国内精品视频一区| 久久久精品在线观看| 国产一区二区丝袜| 亚洲精品suv精品一区二区| 欧美激情乱人伦一区| 国产成人免费91av在线| 伊人久久精品视频| 亚洲午夜久久久影院| 亚洲欧洲美洲在线综合| 欧美精品久久久久久久久| 亚洲欧美日韩高清| 亚洲激情视频网站| 日韩美女视频中文字幕| 国内精品在线一区| 欧美日韩激情网| 日韩在线中文字| 国产丝袜一区二区三区免费视频| 欧美理论在线观看| 97婷婷大伊香蕉精品视频| 欧美日韩亚洲一区二区| 日韩精品小视频| 日韩欧美在线字幕| 欧美日韩另类字幕中文| 久久久国产一区二区| 国产69久久精品成人| 欧美在线观看一区二区三区| 中文字幕九色91在线| 情事1991在线| 久久久久久噜噜噜久久久精品| 国产在线98福利播放视频| 日韩视频免费看| 亚洲第一综合天堂另类专| 欧美与黑人午夜性猛交久久久| 日韩av在线免费观看| 最新69国产成人精品视频免费| 久久频这里精品99香蕉| 日本国产高清不卡| 日韩欧美中文字幕在线播放| 亚洲a∨日韩av高清在线观看| 国产美女直播视频一区| 亚洲精品成人久久| 粉嫩老牛aⅴ一区二区三区| 国产不卡av在线| 日韩少妇与小伙激情| 亚洲欧美日韩一区二区在线| 91国偷自产一区二区三区的观看方式| 97人洗澡人人免费公开视频碰碰碰| 欧美日韩国产123| 久久精品电影一区二区| 日韩激情视频在线| 久久91精品国产91久久久| 国产精品爽爽爽| 国产精品1234| 91成人免费观看网站| 黄色成人在线免费| 97激碰免费视频| 亚洲精品乱码久久久久久金桔影视| 国产欧美日韩高清| 国产在线高清精品| 中文字幕久热精品视频在线| 日韩中文在线中文网在线观看| 国产视频精品久久久| 亚洲欧美制服第一页| 国产精品久久久久久久久久三级| 国产女同一区二区| 97久久久久久| 久久艹在线视频| 亚洲国产精品小视频| 久久免费福利视频| 精品动漫一区二区| 亚洲精品之草原avav久久| 欧美电影免费看| 欧美福利小视频| 亚洲成**性毛茸茸| 亚洲图片欧美午夜| 亚洲高清一二三区| 成人欧美一区二区三区在线| 国产精品视频在线观看| 免费不卡欧美自拍视频| 国产精品久久不能| 国产v综合v亚洲欧美久久| 亚洲人成在线播放| 国产在线观看精品| 国产精品日韩欧美综合| 欧美午夜精品久久久久久浪潮| 中文日韩电影网站| 美女久久久久久久| 激情成人中文字幕| 亚洲人成电影在线| 国产日韩欧美在线观看| 国产亚洲美女精品久久久| 欧美孕妇与黑人孕交| 国产精品嫩草视频|