读目录
头文件<dirent.h>相关函数介绍
对某个目录具有访问权限的任何用户都可以读目录
但是,为了防止文件系统产生混乱,只有内核才能写目录
一个目录的写权限位和执行权限位决定了在该目录中能否创建新文件以及删除文件,并不是能否写目录本身
UNIX现在包含了一套与目录有关的程序,它们是POSIX.1的一部分。很多实现阻止应用程序使用read函数去读取目录的内容,由此进一步将应用程序与目录中实现相关的细节隔离。
#include <dirent.h>
DIR *opendir(const char *pathname);
- DIR *fdopendir(int fd);
- Both return: 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: 0 if OK, ?1 on error
long 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
* 作者: [email protected]
*/
#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 filename
typedef int MyFunc(const char *, const struct stat *, int);
static MyFunc g_myFunc;
static char *g_fullPath;
static size_t g_pathLen;
static long g_nTotal = 0L;
static long g_nReg = 0L;
static long g_nDir = 0L;
static long g_nBlk = 0L;
static long g_nChr = 0L;
static long g_nFifo = 0L;
static long g_nSlink = 0L;
static long 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 */
static void err_doit(int, int, const char *, va_list);
static int myFtw(char *, MyFunc *);
static int doPath(MyFunc *myFunc);
/**
* Print a message and return to caller.
* Caller specifies "errnoflag".
*/
static void err_doit(int errnoflag, int error, const char *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, "\n");
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.
*/
static void err_quit(const char *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.
*/
static void err_ret(const char *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.
*/
static void err_sys(const char *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.
*/
static void err_dump(const char *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 %%\n", g_nReg, g_nReg * 100.0 / g_nTotal);
printf("directories = %7ld, %5.2f %%\n", g_nDir, g_nDir * 100.0 / g_nTotal);
printf("block special = %7ld, %5.2f %%\n", g_nBlk, g_nBlk * 100.0 / g_nTotal);
printf("char special = %7ld, %5.2f %%\n", g_nChr, g_nChr * 100.0 / g_nTotal);
printf("FIFOS = %7ld, %5.2f %%\n", g_nFifo, g_nFifo * 100.0 / g_nTotal);
printf("symbolic links = %7ld, %5.2f %%\n", g_nSlink, g_nSlink * 100.0 / g_nTotal);
printf("sockets = %7ld, %5.2f %%\n", g_nSock, g_nSock * 100.0 / g_nTotal);
// remember free
if (g_fullPath != NULL)
{
free(g_fullPath);
g_fullPath = NULL;
}
exit(ret);
}
static long g_posix_version = 0L;
static long g_xsi_version = 0L;
#ifdef PATH_MAX
static long g_pathMax = PATH_MAX;
#else
static long g_pathMax = 0;
#endif
/* If PATH_MAX is indeterminate, no guarantee this is adequate */
#define PATH_MAX_GUESS 1024
char *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);
}
static int 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
*/
static int 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] = ‘\0‘;
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] = ‘\0‘; // erase everything from slash onward
if (closedir(dp) < 0)
{
err_ret("can‘t close directory %s", g_fullPath);
}
return ret;
}
static int ftw_f(const char *pathName, const struct 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_D
err_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;
}
}
static int g_myFunc(const char *pathName, const struct 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;
}
return 0;
}
图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 读目录