《APUE》:递归遍历目录层次结构,并按文件类型计数


《Unix环境高级编程》这本书附带了许多短小精美的小程序,我在阅读此书的时候,将书上的代码按照自己的理解重写了一遍(大部分是抄书上的),加深一下自己的理解(纯看书太困了,呵呵)。此例子在Ubuntu 10.04上测试通过。

相关链接

  • 《UNIX环境高级编程》(第二版)apue.h的错误
  • Unix环境高级编程 源代码地址
  1. //《APUE》程序4-7:   
  2. //递归遍历目录层次结构,并按文件类型计数   
  3. #include <unistd.h>   
  4. #include <utime.h>   
  5. #include <dirent.h>   
  6. #include <sys/stat.h>   
  7. #include <stdio.h>   
  8. #include <limits.h>   
  9. #include <stdlib.h>   
  10. #include <string.h>   
  11.   
  12. typedef int Myfunc(const char*, const struct stat*, int);  
  13. static Myfunc myfunc;  
  14. static int myftw(char*, Myfunc*);  
  15. static int dopath(Myfunc*);  
  16. static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;  
  17.   
  18. int main(int argc, char **argv)  
  19. {  
  20.     int ret;  
  21.     if( argc != 2 )  
  22.     {  
  23.         fprintf(stderr, "Usage: ftw <starting-pathname>\n");  
  24.         exit(1);  
  25.     }  
  26.     ret = myftw(argv[1], myfunc);  
  27.     ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;  
  28.     //   
  29.     if( 0 == ntot )  
  30.         ntot = 1;  
  31.   
  32.     //输入各种文件的数量及所占的比例   
  33.     printf("reglar files = %7ld, %5.2lf %% \n",  
  34.         nreg, nreg*100.0/ntot);  
  35.     printf("directories = %7ld, %5.2lf %% \n",  
  36.         ndir, ndir*100.0/ntot);  
  37.     printf("block special = %7ld, %5.2lf %% \n",  
  38.         nblk, nblk*100.0/ntot);  
  39.     printf("char special = %7ld, %5.2lf %% \n",  
  40.         nchr, nchr*100.0/ntot);  
  41.     printf("FIFOs = %7ld, %5.2lf %% \n",  
  42.         nfifo, nfifo*100.0/ntot);  
  43.     printf("symbolic links = %7ld, %5.2lf %% \n",  
  44.         nslink, nslink*100.0/ntot);  
  45.     printf("sockets = %7ld, %5.2lf %% \n",  
  46.         nsock, nsock*100.0/ntot);  
  47.     return ret;  
  48. }  
  49.   
  50. #define FTW_F 1   
  51. #define FTW_D 2   
  52. #define FTW_DNR 3   
  53. #define FTW_NS 4   
  54.   
  55. static char *fullpath;  
  56.   
  57. static int myftw(char *pathname, Myfunc *func)  
  58. {  
  59.     //《APUE》书中,这个功能用了一个很复杂的函数path_alloc()来实现   
  60.     //这里我为了简单起见,直接为它分配了一段内存完事   
  61. #ifdef PATH_MAX   
  62.     const int PATH_LEN = PATH_MAX;  
  63. #else   
  64.     const int PATH_LEN = 1024;  
  65. #endif   
  66.   
  67.     fullpath = malloc(PATH_LEN);  
  68.     strncpy(fullpath, pathname, PATH_LEN);  
  69.     fullpath[PATH_LEN-1] = '\0';  
  70.     int res = dopath(func);  
  71.     //《APUE》书中,好像没有释放这段内存   
  72.     free(fullpath);  
  73.     return res;  
  74. }  
  75.   
  76. static int dopath(Myfunc* func)  
  77. {  
  78.     struct stat statbuf;  
  79.     struct dirent *dirp;  
  80.     DIR *dp;  
  81.     int ret;  
  82.     char *ptr;  
  83.   
  84.     int temp;  
  85.     temp = lstat(fullpath, &statbuf);  
  86.     //文件状态错误   
  87.     if( temp < 0 )  
  88.         return func(fullpath, &statbuf, FTW_NS);  
  89.     temp = S_ISDIR(statbuf.st_mode);  
  90.     //不是文件夹   
  91.     if( 0 == temp )  
  92.         return func(fullpath, &statbuf, FTW_F);  
  93.   
  94.     ret = func(fullpath, &statbuf, FTW_D);  
  95.     if( ret != 0 )  
  96.         return ret;  
  97.     ptr = fullpath + strlen(fullpath);  
  98.     *ptr++ = '/';  
  99.     *ptr = 0;  
  100.   
  101.     dp = opendir(fullpath);  
  102.     //不能读取该文件夹   
  103.     if( NULL == dp )  
  104.         return func(fullpath, &statbuf, FTW_DNR);  
  105.       
  106.     while( (dirp = readdir(dp)) != NULL )  
  107.     {  
  108.         //忽略.和..这两个文件夹   
  109.         if( strcmp(dirp->d_name, ".") == 0 ||  
  110.             strcmp(dirp->d_name, "..") == 0 )  
  111.             continue;  
  112.         strcpy(ptr, dirp->d_name);  
  113.         //递归遍历各个子文件夹   
  114.         ret = dopath(func);  
  115.         if( ret != 0 )  
  116.             break;  
  117.     }  
  118.   
  119.     ptr[-1] = 0;  
  120.     if( closedir(dp) < 0 )  
  121.     {  
  122.         fprintf(stderr, "can't close directory %s\n", fullpath);  
  123.     }  
  124.     return ret;  
  125. }  
  126.   
  127. static int myfunc(const char *pathname, const struct stat *statptr, int type)  
  128. {  
  129.     switch(type)  
  130.     {  
  131.     case FTW_F:  
  132.         switch( statptr->st_mode & S_IFMT )  
  133.         {  
  134.         case S_IFREG:   
  135.             nreg++;   
  136.             printf("reg: %s\n", fullpath);   
  137.             break;  
  138.         case S_IFBLK:   
  139.             nblk++;   
  140.             printf("blk: %s\n", fullpath);   
  141.             break;  
  142.         case S_IFCHR:   
  143.             nchr++;   
  144.             printf("chr: %s\n", fullpath);  
  145.             break;  
  146.         case S_IFIFO:   
  147.             nfifo++;   
  148.             printf("fifo: %s\n", fullpath);  
  149.             break;  
  150.         case S_IFLNK:   
  151.             nslink++;   
  152.             printf("slink: %s\n", fullpath);  
  153.             break;  
  154.         case S_IFSOCK:   
  155.             nsock++;   
  156.             printf("socket: %s\n", fullpath);  
  157.             break;  
  158.         case S_IFDIR:   
  159.             fprintf(stderr, "For S_IFDIR for %s\n", pathname);  
  160.             exit(1);  
  161.         }  
  162.         //《APUE》书中没有输出遍历的结果,这个是我自己加上去的   
  163.         break;  
  164.     case FTW_D:  
  165.         ndir++;   
  166.         printf("DIR: %s\n", fullpath);  
  167.         break;  
  168.     case FTW_DNR:  
  169.         fprintf(stderr, "can't read directory %s\n", pathname);  
  170.         break;  
  171.     case FTW_NS:  
  172.         fprintf(stderr, "stat error for %s\n", pathname);  
  173.         break;  
  174.     default:  
  175.         fprintf(stderr, "unkown type %d for pathname %s\n",   
  176.             type, pathname);  
  177.     }  
  178.     return 0;  
  179. }  

运行示例(加下划线的为输入):

www.bkjia.com @ubuntu:~/code$ gcc temp.c -o temp

www.bkjia.com @ubuntu:~/code$ ./temp /etc

DIR: /etc
DIR: /etc/sudoers.d
reg: /etc/sudoers.d/README

...............

reglar files =    1593, 59.89 %
directories =     306, 11.50 %
block special =       0,  0.00 %
char special =       0,  0.00 %
FIFOs =       0,  0.00 %
symbolic links =     761, 28.61 %
sockets =       0,  0.00 %

相关内容