一、du命令解析
Summarize disk usage of the set of FILEs, recursively for directories.
du 命令用于输出文件所占用的磁盘空间
默认情况下,它会输出当前目录下(包括该目录的所有子目录下)的所有文件的大小总和,以 1024B 为单位
也可指定路径。若指定的路径为目录, 则输出该目录下所有文件大小的总和;若指定的路径为文件,则输出该文件大小。均以 1024B 为单位
二、类 du 命令实现
我们希望实现一个命令,该命令能够按照如下使用方式使用,统计 path 所占的磁盘空间(以1024B为单位)
mydu path
2.1 如果 path 为普通文件
先考虑实现输出普通文件大小的功能
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>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 <pathname>\n", argv[0]);exit(1);}printf("%ld\n", mydu(argv[1]));exit(0);
}
2.2 如果 path 为目录
再考虑实现输出目录下所有文件大小之和的功能
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <glob.h>
#include <string.h>
#define PATHSIZE 1024static 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 <pathname>\n", argv[0]);exit(1);}printf("%ld\n", mydu(argv[1])/2); // 打印的时候才除以2,避免递归过程中除多了exit(0);
}
对比验证,针对目录统计出来的结果与命令 du 相同
tail -1 指的仅输出最后一行
补充
- 1、程序中 path_noloop 是干什么用的?
先想想我们处理 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;}
- 2、代码有办法优化吗
有办法。因为递归调用需要频繁利用栈空间,而进程允许的栈空间大小是有上限的(可通过命令 ulimit -a 查看)。我们可以将某些栈空间的数据放在全局区(静态区), 节约栈空间
原则:如果一个变量的使用仅在递归点之前,则该变量可以放在静态区存放
优化代码如下
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <glob.h>
#include <string.h>
#define PATHSIZE 1024static 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 <pathname>\n", argv[0]);exit(1);}printf("%ld\n", mydu(argv[1])/2); // 打印的时候才除以2,避免递归过程中除多了exit(0);
}
哒咩哒咩哒咩哒咩哒咩哒咩~~~~