目录
Summarize disk usage of the set of FILEs, recursively for directories.
du 命令用于输出文件所占用的磁盘空间
默认情况下,它会输出当前目录下(包括该目录的所有子目录下)的所有文件的大小总和,以 1024B 为单位
也可指定路径。若指定的路径为目录, 则输出该目录下所有文件大小的总和;若指定的路径为文件,则输出该文件大小。均以 1024B 为单位
我们希望实现一个命令,该命令能够按照如下使用方式使用,统计 path 所占的磁盘空间(以1024B为单位)
mydu path
先考虑实现输出普通文件大小的功能
- #include
- #include
- #include
- #include
- #include
-
-
- static int64_t mydu(const char *path) {
-
- struct stat statbuf;
-
- if (lstat(path, &statbuf) < 0) {
- perror("lstat()");
- exit(1);
- }
-
- if (!S_ISDIR(statbuf.st_mode)) // 如果为普通文件
- return statbuf.st_blocks / 2; // 为什么要除以2?
- // 因为stat结构体中的st_blocks成员统计的是文件占了多少个大小为512B的块
- // 而du统计的单位为1024B,因此需要除以2
- }
-
- int main(int argc, char * argv[]) {
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s
\n" , argv[0]); - exit(1);
- }
-
- printf("%ld\n", mydu(argv[1]));
-
- exit(0);
- }

再考虑实现输出目录下所有文件大小之和的功能
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define PATHSIZE 1024
-
- static int path_noloop(const char *path) { // 避免无限递归
-
- char * pos = strrchr(path, '/');
-
- if (pos == NULL)
- exit(1);
-
- if (strcmp(pos + 1, ".") == 0 || strcmp(pos+1, "..") == 0)
- return 0;
-
- return 1;
-
- }
-
- static int64_t mydu(const char *path) {
-
- struct stat statbuf;
-
- if (lstat(path, &statbuf) < 0) {
- perror("lstat()");
- exit(1);
- }
-
- if (!S_ISDIR(statbuf.st_mode))
- return statbuf.st_blocks; // 当path为普通文件,不用后续递归了
-
- //
- // 下面情况考虑path为目录
- //
-
- char nextpath[PATHSIZE];
- glob_t globbuf;
-
- strncpy(nextpath, path, PATHSIZE);
- strncat(nextpath, "/*", PATHSIZE); // 将path名拓展为"/dir/*"
-
- glob(nextpath, 0, NULL, &globbuf); // 解析该path目录下的所有非隐藏名字
-
- strncpy(nextpath, path, PATHSIZE);
- strncat(nextpath, "/.*", PATHSIZE); // 将path名拓展为"/dir/.*"
-
- glob(nextpath, GLOB_APPEND, NULL, &globbuf); // 解析该path目录下的所有隐藏名字,并添加到已解析的名字集
-
- int64_t sum = 0;
-
- for (int i = 0; i < globbuf.gl_pathc; ++i) {
-
- if (path_noloop(globbuf.gl_pathv[i]))
- sum += mydu(globbuf.gl_pathv[i]); // 递归,获取某个名字下的文件大小可以通过该函数本身实现
-
- }
-
- globfree(&globbuf);
-
- return sum;
-
- }
-
- int main(int argc, char * argv[]) {
-
- if (argc < 2) {
-
- fprintf(stderr, "Usage: %s
\n" , argv[0]); - exit(1);
- }
-
- printf("%ld\n", mydu(argv[1])/2); // 打印的时候才除以2,避免递归过程中除多了
-
- exit(0);
- }

对比验证,针对目录统计出来的结果与命令 du 相同
tail -1 指的仅输出最后一行
先想想我们处理 path 为目录时的递归思路:

解析某一个目录下的名字可以通过调用递归函数本身实现,用分解问题的思想遍历树,看似没啥问题
但是有一点需要注意:某个目录下的名字包含其自身和上一级菜单!
也就是如果我们不注意这一点,遍历树的过程就会像下面这样:

所以,需要通过下面的函数,判断 path 是不是以 "." 或者 ".." 结尾的(即是否指向路径所表示的目录本身或上一级),如果是,则不从这条路进入递归
- static int path_noloop(const char *path) { // 避免无限递归
-
- char * pos = strrchr(path, '/');
-
- if (pos == NULL)
- exit(1);
-
- if (strcmp(pos + 1, ".") == 0 || strcmp(pos+1, "..") == 0)
- return 0;
-
- return 1;
-
- }
有办法。因为递归调用需要频繁利用栈空间,而进程允许的栈空间大小是有上限的(可通过命令 ulimit -a 查看)。我们可以将某些栈空间的数据放在全局区(静态区), 节约栈空间
原则:如果一个变量的使用仅在递归点之前,则该变量可以放在静态区存放
优化代码如下
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define PATHSIZE 1024
-
- static int path_noloop(const char *path) { // 避免无限递归
-
- char * pos = strrchr(path, '/');
-
- if (pos == NULL)
- exit(1);
-
- if (strcmp(pos + 1, ".") == 0 || strcmp(pos+1, "..") == 0)
- return 0;
-
- return 1;
-
- }
-
- static int64_t mydu(const char *path) {
-
- static struct stat statbuf;
-
- if (lstat(path, &statbuf) < 0) {
- perror("lstat()");
- exit(1);
- }
-
- if (!S_ISDIR(statbuf.st_mode))
- return statbuf.st_blocks; // 当path为普通文件,不用后续递归了
-
- //
- // 下面情况考虑path为目录
- //
-
- static char nextpath[PATHSIZE];
- glob_t globbuf;
-
- strncpy(nextpath, path, PATHSIZE);
- strncat(nextpath, "/*", PATHSIZE); // 将path名拓展为"/dir/*"
-
- glob(nextpath, 0, NULL, &globbuf); // 解析该path目录下的所有非隐藏名字
-
- strncpy(nextpath, path, PATHSIZE);
- strncat(nextpath, "/.*", PATHSIZE); // 将path名拓展为"/dir/.*"
-
- glob(nextpath, GLOB_APPEND, NULL, &globbuf); // 解析该path目录下的所有隐藏名字,并添加到已解析的名字集
-
- int64_t sum = 0;
-
- for (int i = 0; i < globbuf.gl_pathc; ++i) {
-
- if (path_noloop(globbuf.gl_pathv[i]))
- sum += mydu(globbuf.gl_pathv[i]); // 递归,获取某个名字下的文件大小可以通过该函数本身实现
-
- }
-
- globfree(&globbuf);
-
- return sum;
-
- }
-
- int main(int argc, char * argv[]) {
-
- if (argc < 2) {
-
- fprintf(stderr, "Usage: %s
\n" , argv[0]); - exit(1);
- }
-
- printf("%ld\n", mydu(argv[1])/2); // 打印的时候才除以2,避免递归过程中除多了
-
- exit(0);
- }
哒咩哒咩哒咩哒咩哒咩哒咩~~~~