1、手动实现递归
1.1 stat()函数族
stat函数族包括:stat,fstat以及lstat函数,都是向用户返回文件的属性信息(元数据)。
复制代码 代码如下:
view plaincopy to clipboardprint?
#include
int stat(const char*pathname,struct stat*buf);
int fstat(int filedes,struct stat*buf);
int lstat(const char *pathname,struct stat*buf);
#include
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;
dev st_dev;
dev st_rdev;
nlink st_nlink;
uid st_uid;
gid st_gid;
off st_size;
time st_atime;
time st_mtime;
time st_ctime;
long st_blksize;
long st_blocks;
#include
#include
#include
#include
#include
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)) {
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s//n",depth,"",entry->d_name);
printdir(entry->d_name,depth+4);
}
else printf("%*s%s/n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
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);
}
#include
#include
#include
#include
#include
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)) {
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s//n",depth,"",entry->d_name);
printdir(entry->d_name,depth+4);
}
else printf("%*s%s/n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
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
int ftw(const char *dirpath,
int (*fn) (const char *fpath, const struct stat *sb,int typeflag),
int nopenfd);
#define _XOPEN_SOURCE 500
#include
int nftw(const char *dirpath,
int (*fn) (const char *fpath, const struct stat *sb,int typeflag, struct FTW *ftwbuf),
int nopenfd, int flags);
#include
int ftw(const char *dirpath,
int (*fn) (const char *fpath, const struct stat *sb,int typeflag),
int nopenfd);
#define _XOPEN_SOURCE 500
#include
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
#include
#include
#include
#include
#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;
}



