欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2013年05月31日 15:09:11   作者:  
本篇文章是對linux下遍歷目錄樹的方法進行了詳細的總結(jié)與分析,需要的朋友參考下
前幾天需要實現(xiàn)對整個目錄樹的遍歷,查閱了相關(guān)的一些資料。開始找到的原始的方法是使用readdir()與lstat()函數(shù)實現(xiàn)遞歸遍歷,后來發(fā)現(xiàn)linux對于目錄遍歷這種最常用的操作已經(jīng)提供了很完善的接口:ftw()與nftw()。下面就這兩種方法具體說明一下。
1、手動實現(xiàn)遞歸
1.1 stat()函數(shù)族
stat函數(shù)族包括:stat,fstat以及l(fā)stat函數(shù),都是向用戶返回文件的屬性信息(元數(shù)據(jù))。
復(fù)制代碼 代碼如下:

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

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

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

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

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

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調(diào)用遍歷目錄
2.1ftw函數(shù)族
使用readdir函數(shù)等實現(xiàn)遞歸遍歷目錄樹的方法比較原始,glibc2.1收錄了ftw等函數(shù),可以方便實現(xiàn)目錄樹的遍歷。
復(fù)制代碼 代碼如下:

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()
函數(shù)說明:ftw() 會從參數(shù)dirpath指定的目錄開始,往下一層層地遞歸式遍歷子目錄。
ftw()會傳三個參數(shù)給fn(), 第一個參數(shù)*fpath指向當時所在的目錄路徑,第二個參數(shù)是*sb, 為stat結(jié)構(gòu)指針,第三個參數(shù)為flag,有下面幾種可能值
FTW_F        一般文件
FTW_D       目錄
FTW_DNR    不可讀取的目錄,此目錄以下將不被遍歷
FTW_SL       符號連接
FTW_NS       無法取得stat結(jié)構(gòu)數(shù)據(jù),有可能是權(quán)限問題
最后一個參數(shù)depth代表ftw()在進行遍歷目錄時同時打開的文件數(shù)。ftw()在遍歷時每一層目錄至少需要一個文件描述詞,如果遍歷時用完了depth所給予的限制數(shù)目,整個遍歷將因不斷地關(guān)文件和開文件操作而顯得緩慢。(實際做測試的時候未發(fā)現(xiàn)...)

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

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

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

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;  


相關(guān)文章

最新評論