2、使用ftw调用遍历目录
2.1ftw函数族
使用readdir函数等实现递归遍历目录树的方法比较原始,glibc2.1收录了ftw等函数,可以方便实现目录树的遍历。
#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,否则会出现未定义等错误。关于这个问题,可以参考文章:http://linuxkernel.lupaworld.com/home-space-uid-30912-do-blog-id-24034.html
有一个没搞清楚的问题是我使用FTW_DEPTH 来遍历整个目录树的时候,遍历到proc目录下存在异常返回,可能还需要指定FTW_PHYS使其不遍历符号链接目录,这个有空查一下。
2、遍历的例子
自己写的一个测试的小例子。遍历指定目录,输出文件元数据和遍历深度等信息。
#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; }