麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 編程 > C > 正文

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

2020-01-26 16:02:09
字體:
供稿:網(wǎng)友
前幾天需要實(shí)現(xiàn)對(duì)整個(gè)目錄樹的遍歷,查閱了相關(guān)的一些資料。開始找到的原始的方法是使用readdir()與lstat()函數(shù)實(shí)現(xiàn)遞歸遍歷,后來發(fā)現(xiàn)linux對(duì)于目錄遍歷這種最常用的操作已經(jīng)提供了很完善的接口:ftw()與nftw()。下面就這兩種方法具體說明一下。
1、手動(dòng)實(shí)現(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);

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

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

下面是一個(gè)簡(jiǎn)單的測(cè)試
復(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;
}

這里補(bǔ)充說明一下linux中文件的基本類型。
1.普通文件(Regular file)。這是最常見的文件類型,這種文件包含了某種形式的數(shù)據(jù)。至于這種數(shù)據(jù)是文本還是二進(jìn)制數(shù)據(jù)對(duì)于系統(tǒng)核而言并無區(qū)別。對(duì)普通文件內(nèi)容的解釋由處理該文件的應(yīng)用程序進(jìn)行。
2.目錄文件(Directory file)。這種文件包含了其它文件的名字以及指向與這些文件有關(guān)信息的指針。對(duì)一個(gè)目錄文件具有讀許可數(shù)的任一進(jìn)程都可以讀該目錄的內(nèi)容,但只有系統(tǒng)核可以寫目錄文件。
3.字符特殊文件(Charocter special file)。這種文件用于系統(tǒng)中的某些類型的設(shè)備。
4.塊特殊文件(Block special file)。這種文件典型地用于磁盤設(shè)備。系統(tǒng)中的所有設(shè)備或者是字符特殊文件,或者是塊特殊文件。
5.FIFO。這種文件用于進(jìn)程間的通信,有時(shí)也將其稱為命名管道。
6.套接口(socket)。這種文件用于進(jìn)程間的網(wǎng)絡(luò)通信。套接口也可用于在一臺(tái)宿主機(jī)上的進(jìn)程之間的非網(wǎng)絡(luò)通信。
7.符號(hào)連接(Symboliclink)。這種文件指向另一個(gè)文件。
對(duì)于文件類型,可以利用定義的宏比如S_ISDIR()等測(cè)試st_mode,判斷文件類型。宏有S_ISREG、S_ISDIR、S_ISCHR、S_ISBLK、S_ISFIFO、S_ISLNK、S_ISSOCK。
1.2 遍歷目錄例子
引用別人的一個(gè)例子,現(xiàn)在把許多文件處理函數(shù)集中在一起使用,程序遍歷指定目錄的文件,同時(shí)也要進(jìn)到下級(jí)子目錄中進(jìn)行遍歷,這一點(diǎn)是將子目錄遞歸傳遞到opendir中去,需要指出的是,這就決定了如果子目錄嵌套過深,程序?qū)⑹》祷兀驗(yàn)樵试S打開的子目錄流數(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ù)等實(shí)現(xiàn)遞歸遍歷目錄樹的方法比較原始,glibc2.1收錄了ftw等函數(shù),可以方便實(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() 會(huì)從參數(shù)dirpath指定的目錄開始,往下一層層地遞歸式遍歷子目錄。
ftw()會(huì)傳三個(gè)參數(shù)給fn(), 第一個(gè)參數(shù)*fpath指向當(dāng)時(shí)所在的目錄路徑,第二個(gè)參數(shù)是*sb, 為stat結(jié)構(gòu)指針,第三個(gè)參數(shù)為flag,有下面幾種可能值
FTW_F        一般文件
FTW_D       目錄
FTW_DNR    不可讀取的目錄,此目錄以下將不被遍歷
FTW_SL       符號(hào)連接
FTW_NS       無法取得stat結(jié)構(gòu)數(shù)據(jù),有可能是權(quán)限問題
最后一個(gè)參數(shù)depth代表ftw()在進(jìn)行遍歷目錄時(shí)同時(shí)打開的文件數(shù)。ftw()在遍歷時(shí)每一層目錄至少需要一個(gè)文件描述詞,如果遍歷時(shí)用完了depth所給予的限制數(shù)目,整個(gè)遍歷將因不斷地關(guān)文件和開文件操作而顯得緩慢。(實(shí)際做測(cè)試的時(shí)候未發(fā)現(xiàn)...)

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

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

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


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 日韩每日更新 | 国产亚洲精品精 | 国产精品一区在线观看 | 看片一区二区三区 | 91成人免费看片 | 亚洲xxx在线观看 | h视频在线观看免费 | 国产毛片视频 | 久久久久久99 | 成人免费视频视频在线观看 免费 | 免费毛片在线 | 欧产日产国产精品v | 欧美成人免费电影 | 在线中文字幕观看 | 中国漂亮护士一级a毛片 | 视频一区二区三区免费观看 | 中文字幕爱爱视频 | 久久艳片| 亚州视频在线 | 成人福利在线播放 | 日本综合久久 | 亚洲福利视频52 | omofun 动漫在线观看 | 精品一二三区视频 | 亚洲精品欧美在线 | 性少妇videosexfreexx入片 | 欧美精品一区自拍a毛片在线视频 | 欧产日产国产精品乱噜噜 | 鲁丝一区二区二区四区 | 精品一区二区久久久久久按摩 | 羞羞电影在线观看www | 国产精品91久久久 | 最新黄色av | 久久欧美亚洲另类专区91大神 | 亚洲成人免费影视 | 亚洲一区免费观看 | 欧美日韩一 | 国产午夜精品理论片a级探花 | 神马福利网 | 蜜桃视频网站www | 欧美videofree性欧美另类 |