读目录
头文件<dirent.h>相关函数介绍
对某个目录具有访问权限的任何用户都可以读目录
但是,为了防止文件系统产生混乱,只有内核才能写目录
一个目录的写权限位和执行权限位决定了在该目录中能否创建新文件以及删除文件,并不是能否写目录本身
UNIX现在包含了一套与目录有关的程序,它们是POSIX.1的一部分。很多实现阻止应用程序使用read函数去读取目录的内容,由此进一步将应用程序与目录中实现相关的细节隔离。
#include<dirent.h>DIR*opendir(constchar*pathname);- DIR*fdopendir(int fd);
- Bothreturn: pointer if OK, NULL on error
struct dirent *readdir(DIR*dp);- Returns: pointer if OK, NULL at end of directory or error
void rewinddir(DIR*dp);- int closedir(DIR*dp);
Returns:0if OK,−1 on errorlong telldir(DIR*dp);- Returns: current location in directory associated with dp
void seekdir(DIR*dp,long loc);
图1 <dirent.h>文件里的函数原型
telldir和seekdir函数不是基本POSIX.1标准组成部分,是Single UNIX Specification中的XSI扩展
定义在<dirent.h>头文件中的dirent结构与实现有关,实现此结构体的定义至少包含下列两个成员:
ino_t d_ino; /* i-node number */
char d_name[]; /* null-terminated filename */
注意,d_name项的大小没有指定,但必须保证它能包含至少NAME_MAX个字节(不包含终止null字节)
DIR结构是一个内部结构,上述7个函数用这个内部结构保存当前正在被读的目录的有关信息
由opendir和fdopendir返回的指向DIR结构的指针由另外5个函数使用
opendir执行初始化操作,使第一个readdir返回目录中的第一个目录项
注意,目录中各个目录项的顺序与实现有关,它们通常并不按字母顺序排序
实例
编写一个遍历文件层次结构的程序,并统计各种类型文件的数量
/*** 文件内容: 编写一个遍历文件层次结构的程序,并统计各种类型文件的数量* 文件时间: 2016年 11月 13日 星期日 15:30:31 CST* 作者: firewaywei@126.com*/#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<unistd.h>#include<dirent.h>#include<string.h>#include<stdarg.h>// ISO C variable aruments#include<stdlib.h>#include<errno.h>- #define MAXLINE 4096// max line length
// function type that is called for each filenametypedefintMyFunc(constchar*,conststruct stat *,int);staticMyFunc g_myFunc;staticchar*g_fullPath;staticsize_t g_pathLen;staticlong g_nTotal =0L;staticlong g_nReg =0L;staticlong g_nDir =0L;staticlong g_nBlk =0L;staticlong g_nChr =0L;staticlong g_nFifo =0L;staticlong g_nSlink =0L;staticlong g_nSock =0L;#define FTW_F 1/* file other than directory */#define FTW_D 2/* directory */#define FTW_DNR 3/* directory that can't be read */#define FTW_NS 4/* file that we can't stat */staticvoid err_doit(int,int,constchar*, va_list);staticint myFtw(char*,MyFunc*);staticint doPath(MyFunc*myFunc);/*** Print a message and return to caller.* Caller specifies "errnoflag".*/staticvoid err_doit(int errnoflag,int error,constchar*fmt, va_list ap){char buf[MAXLINE]={0};vsnprintf(buf, MAXLINE -1, fmt, ap);if(errnoflag){snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1,": %s",strerror(error));}strcat(buf," ");fflush(stdout);/* in case stdout and stderr are the same */fputs(buf, stderr);fflush(NULL);/* flushes all stdio output streams */}/*** Fatal error unrelated to a system call.* Print a message and terminate.*/staticvoid err_quit(constchar*fmt,...){va_list ap;va_start(ap, fmt);err_doit(0,0, fmt, ap);va_end(ap);exit(1);}/*** Nonfatal error related to a system call.* Print a message and return.*/staticvoid err_ret(constchar*fmt,...){va_list ap;va_start(ap, fmt);err_doit(1, errno, fmt, ap);va_end(ap);}/*** Fatal error related to a system call.* Print a message and terminate.*/staticvoid err_sys(constchar*fmt,...){va_list ap;va_start(ap, fmt);err_doit(1, errno, fmt, ap);va_end(ap);exit(1);}/*** Fatal error related to a system call.* Print a message, dump core, and terminate.*/staticvoid err_dump(constchar*fmt,...){va_list ap;va_start(ap, fmt);err_doit(1, errno, fmt, ap);va_end(ap);abort();/* dump core and terminate */exit(1);/* shouldn't get here */}int main(int argc,char** argv){int ret =0;if(argc !=2){err_quit("usage: ftw <starting-pathname>");}ret = myFtw(argv[1], g_myFunc);g_nTotal = g_nReg + g_nDir + g_nBlk + g_nChr + g_nFifo + g_nSlink + g_nSock;if(0== g_nTotal){g_nTotal =1L;}printf("regular files = %7ld, %5.2f %% ", g_nReg, g_nReg *100.0/ g_nTotal);printf("directories = %7ld, %5.2f %% ", g_nDir, g_nDir *100.0/ g_nTotal);printf("block special = %7ld, %5.2f %% ", g_nBlk, g_nBlk *100.0/ g_nTotal);printf("char special = %7ld, %5.2f %% ", g_nChr, g_nChr *100.0/ g_nTotal);printf("FIFOS = %7ld, %5.2f %% ", g_nFifo, g_nFifo *100.0/ g_nTotal);printf("symbolic links = %7ld, %5.2f %% ", g_nSlink, g_nSlink *100.0/ g_nTotal);printf("sockets = %7ld, %5.2f %% ", g_nSock, g_nSock *100.0/ g_nTotal);// remember freeif(g_fullPath != NULL){free(g_fullPath);g_fullPath = NULL;}exit(ret);}staticlong g_posix_version =0L;staticlong g_xsi_version =0L;#ifdef PATH_MAXstaticlong g_pathMax = PATH_MAX;#elsestaticlong g_pathMax =0;#endif/* If PATH_MAX is indeterminate, no guarantee this is adequate */#define PATH_MAX_GUESS 1024char*path_alloc(size_t*sizep){char*ptr = NULL;size_t size =0;if(g_posix_version ==0){g_posix_version = sysconf(_SC_VERSION);}if(g_xsi_version ==0){g_xsi_version = sysconf(_SC_XOPEN_VERSION);}if(g_pathMax ==0){/* first time through */errno =0;if((g_pathMax = pathconf("/", _PC_PATH_MAX))<0){if(errno ==0){g_pathMax = PATH_MAX_GUESS;/* it's indeterminate */}else{err_sys("pathconf error for _PC_PATH_MAX");}}else{g_pathMax++;/* add one since it's relative to root */}}/** Before POSIX.1-2001, we aren't guaranteed that PATH_MAX includes* the terminating null byte. Same goes for XPG3.*/if((g_posix_version <200112L)&&(g_xsi_version <4)){size = g_pathMax +1;}else{size = g_pathMax;}if((ptr = malloc(size))== NULL){err_sys("malloc error for pathname");}if((ptr = memset(ptr,0, size))== NULL){err_sys("memset error for pathname");}if(sizep != NULL){*sizep = size;}return(ptr);}staticint myFtw(char*pathName,MyFunc*myFunc){g_fullPath = path_alloc(&g_pathLen);if(g_pathLen < strlen(pathName)){g_pathLen = strlen(pathName)*2;if(realloc(g_fullPath, g_pathLen)== NULL){err_sys("realloc failed");}}strcpy(g_fullPath, pathName);return doPath(myFunc);}/*** we return whatever myFunc return*/staticint doPath(MyFunc*myFunc){struct stat statBuf;if(lstat(g_fullPath,&statBuf)<0)// stat error{return myFunc(g_fullPath,&statBuf, FTW_NS);}if(S_ISDIR(statBuf.st_mode)==0)// not a diretctory{return myFunc(g_fullPath,&statBuf, FTW_F);}// It's a directory, first call myFunc() for the directory,// then process each filename in the directory.int ret =0;if(ret = myFunc(g_fullPath,&statBuf, FTW_D)!=0){return ret;}int n = strlen(g_fullPath);if(n + NAME_MAX +2> g_pathLen)// expand path buffer{g_pathLen *=2;if((g_fullPath = realloc(g_fullPath, g_pathLen))== NULL){err_sys("realloc failed");}}g_fullPath[n++]='/';g_fullPath[n]=' ';DIR*dp = NULL;if((dp = opendir(g_fullPath))== NULL)// can't read directory{return myFunc(g_fullPath,&statBuf, FTW_DNR);}struct dirent *pDir = NULL;while((pDir = readdir(dp))!= NULL){if(0== strcmp(".", pDir->d_name)||0== strcmp("..", pDir->d_name)){continue;// ignore dot and dot-dot}strcpy(&(g_fullPath[n]), pDir->d_name);// append name after "/"if((ret = doPath(myFunc))!=0)// recursive{break;// time to leave}}g_fullPath[n -1]=' ';// erase everything from slash onwardif(closedir(dp)<0){err_ret("can't close directory %s", g_fullPath);}return ret;}staticint ftw_f(constchar*pathName,conststruct stat *statPtr,int type){switch(statPtr->st_mode & S_IFMT){case S_IFREG:g_nReg++;break;case S_IFBLK:g_nBlk++;break;case S_IFCHR:g_nChr++;break;case S_IFIFO:g_nFifo++;break;case S_IFLNK:g_nSlink++;break;case S_IFSOCK:g_nSock++;break;case S_IFDIR:// directories should have type = FTW_Derr_dump("for S_IFDIR for %s, directories should have type = FTW_D", pathName);break;default:err_dump("%s unknown type %d for pathname %s", __FUNCTION__, type, pathName);break;}}staticint g_myFunc(constchar*pathName,conststruct stat *statPtr,int type){switch(type){case FTW_F:ftw_f(pathName, statPtr, type);break;case FTW_D:g_nDir++;break;case FTW_DNR:err_ret("can't read directory %s", pathName);break;case FTW_NS:err_ret("stat error for %s", pathName);break;default:err_dump("%s unknown type %d for pathname %s", __FUNCTION__, type, pathName);break;}return0;}
图2. 递归降序遍历目录层次结构,并按文件类型计数
程序运行如下:
$ ./a.out .
regular files = 4, 33.33 %
directories = 6, 50.00 %
block special = 0, 0.00 %
char special = 0, 0.00 %
FIFOS = 0, 0.00 %
symbolic links = 2, 16.67 %
sockets = 0, 0.00 %
参考
UNIX环境高级编程(第三版) 4.22 读目录