目录
一、du命令解析
二、类 du 命令实现
2.1 如果 path 为普通文件
2.2 如果 path 为目录
补充
一、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 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 <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 查看)。我们可以将某些栈空间的数据放在全局区(静态区), 节约栈空间
原则:如果一个变量的使用仅在递归点之前,则该变量可以放在静态区存放
优化代码如下 文章来源:https://www.toymoban.com/news/detail-728779.html
#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 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 <pathname>\n", argv[0]);
exit(1);
}
printf("%ld\n", mydu(argv[1])/2); // 打印的时候才除以2,避免递归过程中除多了
exit(0);
}
哒咩哒咩哒咩哒咩哒咩哒咩~~~~文章来源地址https://www.toymoban.com/news/detail-728779.html
到了这里,关于【APUE】文件系统 — 类 du 命令功能实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!