程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 深刻linux下遍歷目次樹的辦法總結剖析

深刻linux下遍歷目次樹的辦法總結剖析

編輯:關於C++

深刻linux下遍歷目次樹的辦法總結剖析。本站提示廣大學習愛好者:(深刻linux下遍歷目次樹的辦法總結剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻linux下遍歷目次樹的辦法總結剖析正文


頭幾天須要完成對全部目次樹的遍歷,查閱了相干的一些材料。開端找到的原始的辦法是應用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;  


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved