首页 > 代码库 > 读目录

读目录

读目录

头文件<dirent.h>相关函数介绍

    对某个目录具有访问权限的任何用户都可以读目录
    但是,为了防止文件系统产生混乱,只有内核才能写目录
    一个目录的写权限位和执行权限位决定了在该目录中能否创建新文件以及删除文件,并不是能否写目录本身
    UNIX现在包含了一套与目录有关的程序,它们是POSIX.1的一部分。很多实现阻止应用程序使用read函数去读取目录的内容,由此进一步将应用程序与目录中实现相关的细节隔离。
  1. #include <dirent.h>
  2. DIR *opendir(const char *pathname);
  3. DIR *fdopendir(int fd);
  4. Both return: pointer if OK, NULL on error
  5. struct dirent *readdir(DIR *dp);
  6. Returns: pointer if OK, NULL at end of directory or error
  7. void rewinddir(DIR *dp);
  8. int closedir(DIR *dp);
  9. Returns: 0 if OK, ?1 on error
  10. long telldir(DIR *dp);
  11. Returns: current location in directory associated with dp
  12. void seekdir(DIR *dp, long loc);
图1 <dirent.h>文件里的函数原型
  telldirseekdir函数不是基本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个函数用这个内部结构保存当前正在被读的目录的有关信息
    由opendirfdopendir返回的指向DIR结构的指针由另外5个函数使用
    opendir执行初始化操作,使第一个readdir返回目录中的第一个目录项
    注意,目录中各个目录项的顺序与实现有关,它们通常并不按字母顺序排序

实例

    编写一个遍历文件层次结构的程序,并统计各种类型文件的数量
  1. /**
  2. * 文件内容: 编写一个遍历文件层次结构的程序,并统计各种类型文件的数量
  3. * 文件时间: 2016年 11月 13日 星期日 15:30:31 CST
  4. * 作者: firewaywei@126.com
  5. */
  6. #include <stdio.h>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <unistd.h>
  10. #include <dirent.h>
  11. #include <string.h>
  12. #include <stdarg.h> // ISO C variable aruments
  13. #include <stdlib.h>
  14. #include <errno.h>

  15. #define MAXLINE 4096 // max line length

  16. // function type that is called for each filename
  17. typedef int MyFunc(const char *, const struct stat *, int);
  18. static MyFunc g_myFunc;
  19. static char *g_fullPath;
  20. static size_t g_pathLen;
  21. static long g_nTotal = 0L;
  22. static long g_nReg = 0L;
  23. static long g_nDir = 0L;
  24. static long g_nBlk = 0L;
  25. static long g_nChr = 0L;
  26. static long g_nFifo = 0L;
  27. static long g_nSlink = 0L;
  28. static long g_nSock = 0L;
  29. #define FTW_F 1 /* file other than directory */
  30. #define FTW_D 2 /* directory */
  31. #define FTW_DNR 3 /* directory that can‘t be read */
  32. #define FTW_NS 4 /* file that we can‘t stat */
  33. static void err_doit(int, int, const char *, va_list);
  34. static int myFtw(char *, MyFunc *);
  35. static int doPath(MyFunc *myFunc);
  36. /**
  37. * Print a message and return to caller.
  38. * Caller specifies "errnoflag".
  39. */
  40. static void err_doit(int errnoflag, int error, const char *fmt, va_list ap)
  41. {
  42. char buf[MAXLINE] = { 0 };
  43. vsnprintf(buf, MAXLINE - 1, fmt, ap);
  44. if (errnoflag)
  45. {
  46. snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
  47. strerror(error));
  48. }
  49. strcat(buf, "\n");
  50. fflush(stdout); /* in case stdout and stderr are the same */
  51. fputs(buf, stderr);
  52. fflush(NULL); /* flushes all stdio output streams */
  53. }
  54. /**
  55. * Fatal error unrelated to a system call.
  56. * Print a message and terminate.
  57. */
  58. static void err_quit(const char *fmt, ...)
  59. {
  60. va_list ap;
  61. va_start(ap, fmt);
  62. err_doit(0, 0, fmt, ap);
  63. va_end(ap);
  64. exit(1);
  65. }
  66. /**
  67. * Nonfatal error related to a system call.
  68. * Print a message and return.
  69. */
  70. static void err_ret(const char *fmt, ...)
  71. {
  72. va_list ap;
  73. va_start(ap, fmt);
  74. err_doit(1, errno, fmt, ap);
  75. va_end(ap);
  76. }
  77. /**
  78. * Fatal error related to a system call.
  79. * Print a message and terminate.
  80. */
  81. static void err_sys(const char *fmt, ...)
  82. {
  83. va_list ap;
  84. va_start(ap, fmt);
  85. err_doit(1, errno, fmt, ap);
  86. va_end(ap);
  87. exit(1);
  88. }
  89. /**
  90. * Fatal error related to a system call.
  91. * Print a message, dump core, and terminate.
  92. */
  93. static void err_dump(const char *fmt, ...)
  94. {
  95. va_list ap;
  96. va_start(ap, fmt);
  97. err_doit(1, errno, fmt, ap);
  98. va_end(ap);
  99. abort(); /* dump core and terminate */
  100. exit(1); /* shouldn‘t get here */
  101. }
  102. int main(int argc, char ** argv)
  103. {
  104. int ret = 0;
  105. if (argc != 2)
  106. {
  107. err_quit("usage: ftw <starting-pathname>");
  108. }
  109. ret = myFtw(argv[1], g_myFunc);
  110. g_nTotal = g_nReg + g_nDir + g_nBlk + g_nChr + g_nFifo + g_nSlink + g_nSock;
  111. if (0 == g_nTotal)
  112. {
  113. g_nTotal = 1L;
  114. }
  115. printf("regular files = %7ld, %5.2f %%\n", g_nReg, g_nReg * 100.0 / g_nTotal);
  116. printf("directories = %7ld, %5.2f %%\n", g_nDir, g_nDir * 100.0 / g_nTotal);
  117. printf("block special = %7ld, %5.2f %%\n", g_nBlk, g_nBlk * 100.0 / g_nTotal);
  118. printf("char special = %7ld, %5.2f %%\n", g_nChr, g_nChr * 100.0 / g_nTotal);
  119. printf("FIFOS = %7ld, %5.2f %%\n", g_nFifo, g_nFifo * 100.0 / g_nTotal);
  120. printf("symbolic links = %7ld, %5.2f %%\n", g_nSlink, g_nSlink * 100.0 / g_nTotal);
  121. printf("sockets = %7ld, %5.2f %%\n", g_nSock, g_nSock * 100.0 / g_nTotal);
  122. // remember free
  123. if (g_fullPath != NULL)
  124. {
  125. free(g_fullPath);
  126. g_fullPath = NULL;
  127. }
  128. exit(ret);
  129. }
  130. static long g_posix_version = 0L;
  131. static long g_xsi_version = 0L;
  132. #ifdef PATH_MAX
  133. static long g_pathMax = PATH_MAX;
  134. #else
  135. static long g_pathMax = 0;
  136. #endif
  137. /* If PATH_MAX is indeterminate, no guarantee this is adequate */
  138. #define PATH_MAX_GUESS 1024
  139. char *path_alloc(size_t *sizep)
  140. {
  141. char *ptr = NULL;
  142. size_t size = 0;
  143. if (g_posix_version == 0)
  144. {
  145. g_posix_version = sysconf(_SC_VERSION);
  146. }
  147. if (g_xsi_version == 0)
  148. {
  149. g_xsi_version = sysconf(_SC_XOPEN_VERSION);
  150. }
  151. if (g_pathMax == 0)
  152. {
  153. /* first time through */
  154. errno = 0;
  155. if ((g_pathMax = pathconf("/", _PC_PATH_MAX)) < 0)
  156. {
  157. if (errno == 0)
  158. {
  159. g_pathMax = PATH_MAX_GUESS; /* it‘s indeterminate */
  160. }
  161. else
  162. {
  163. err_sys("pathconf error for _PC_PATH_MAX");
  164. }
  165. }
  166. else
  167. {
  168. g_pathMax++; /* add one since it‘s relative to root */
  169. }
  170. }
  171. /*
  172. * Before POSIX.1-2001, we aren‘t guaranteed that PATH_MAX includes
  173. * the terminating null byte. Same goes for XPG3.
  174. */
  175. if ((g_posix_version < 200112L) && (g_xsi_version < 4))
  176. {
  177. size = g_pathMax + 1;
  178. }
  179. else
  180. {
  181. size = g_pathMax;
  182. }
  183. if ((ptr = malloc(size)) == NULL)
  184. {
  185. err_sys("malloc error for pathname");
  186. }
  187. if ((ptr = memset(ptr, 0, size)) == NULL)
  188. {
  189. err_sys("memset error for pathname");
  190. }
  191. if (sizep != NULL)
  192. {
  193. *sizep = size;
  194. }
  195. return(ptr);
  196. }
  197. static int myFtw(char *pathName, MyFunc *myFunc)
  198. {
  199. g_fullPath = path_alloc(&g_pathLen);
  200. if (g_pathLen < strlen(pathName))
  201. {
  202. g_pathLen = strlen(pathName) * 2;
  203. if (realloc(g_fullPath, g_pathLen) == NULL)
  204. {
  205. err_sys("realloc failed");
  206. }
  207. }
  208. strcpy(g_fullPath, pathName);
  209. return doPath(myFunc);
  210. }
  211. /**
  212. * we return whatever myFunc return
  213. */
  214. static int doPath(MyFunc *myFunc)
  215. {
  216. struct stat statBuf;
  217. if (lstat(g_fullPath, &statBuf) < 0) // stat error
  218. {
  219. return myFunc(g_fullPath, &statBuf, FTW_NS);
  220. }
  221. if (S_ISDIR(statBuf.st_mode) == 0) // not a diretctory
  222. {
  223. return myFunc(g_fullPath, &statBuf, FTW_F);
  224. }
  225. // It‘s a directory, first call myFunc() for the directory,
  226. // then process each filename in the directory.
  227. int ret = 0;
  228. if (ret = myFunc(g_fullPath, &statBuf, FTW_D) != 0)
  229. {
  230. return ret;
  231. }
  232. int n = strlen(g_fullPath);
  233. if (n + NAME_MAX + 2 > g_pathLen) // expand path buffer
  234. {
  235. g_pathLen *= 2;
  236. if ((g_fullPath = realloc(g_fullPath, g_pathLen)) == NULL)
  237. {
  238. err_sys("realloc failed");
  239. }
  240. }
  241. g_fullPath[n++] = ‘/‘;
  242. g_fullPath[n] = ‘\0‘;
  243. DIR *dp = NULL;
  244. if ((dp = opendir(g_fullPath)) == NULL) // can‘t read directory
  245. {
  246. return myFunc(g_fullPath, &statBuf, FTW_DNR);
  247. }
  248. struct dirent *pDir = NULL;
  249. while((pDir = readdir(dp)) != NULL)
  250. {
  251. if (0 == strcmp(".", pDir->d_name) || 0 == strcmp("..", pDir->d_name))
  252. {
  253. continue; // ignore dot and dot-dot
  254. }
  255. strcpy(&(g_fullPath[n]), pDir->d_name); // append name after "/"
  256. if ((ret = doPath(myFunc)) != 0) // recursive
  257. {
  258. break; // time to leave
  259. }
  260. }
  261. g_fullPath[n - 1] = ‘\0‘; // erase everything from slash onward
  262. if (closedir(dp) < 0)
  263. {
  264. err_ret("can‘t close directory %s", g_fullPath);
  265. }
  266. return ret;
  267. }
  268. static int ftw_f(const char *pathName, const struct stat *statPtr, int type)
  269. {
  270. switch(statPtr->st_mode & S_IFMT)
  271. {
  272. case S_IFREG:
  273. g_nReg++;
  274. break;
  275. case S_IFBLK:
  276. g_nBlk++;
  277. break;
  278. case S_IFCHR:
  279. g_nChr++;
  280. break;
  281. case S_IFIFO:
  282. g_nFifo++;
  283. break;
  284. case S_IFLNK:
  285. g_nSlink++;
  286. break;
  287. case S_IFSOCK:
  288. g_nSock++;
  289. break;
  290. case S_IFDIR: // directories should have type = FTW_D
  291. err_dump("for S_IFDIR for %s, directories should have type = FTW_D", pathName);
  292. break;
  293. default:
  294. err_dump("%s unknown type %d for pathname %s", __FUNCTION__, type, pathName);
  295. break;
  296. }
  297. }
  298. static int g_myFunc(const char *pathName, const struct stat *statPtr, int type)
  299. {
  300. switch (type)
  301. {
  302. case FTW_F:
  303. ftw_f(pathName, statPtr, type);
  304. break;
  305. case FTW_D:
  306. g_nDir++;
  307. break;
  308. case FTW_DNR:
  309. err_ret("can‘t read directory %s", pathName);
  310. break;
  311. case FTW_NS:
  312. err_ret("stat error for %s", pathName);
  313. break;
  314. default:
  315. err_dump("%s unknown type %d for pathname %s", __FUNCTION__, type, pathName);
  316. break;
  317. }
  318. return 0;
  319. }
图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 读目录

 

读目录