【APUE】文件系统 — 类 du 命令功能实现

news2024/12/24 18:49:07

一、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 查看)。我们可以将某些栈空间的数据放在全局区(静态区), 节约栈空间

原则:如果一个变量的使用仅在递归点之前,则该变量可以放在静态区存放 

优化代码如下 

#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);
}

 

哒咩哒咩哒咩哒咩哒咩哒咩~~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1060833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

包装机(栈和队列的应用)

一种自动包装机的结构如图 1 所示。首先机器中有 N 条轨道&#xff0c;放置了一些物品。轨道下面有一个筐。当某条轨道的按钮被按下时&#xff0c;活塞向左推动&#xff0c;将轨道尽头的一件物品推落筐中。当 0 号按钮被按下时&#xff0c;机械手将抓取筐顶部的一件物品&#x…

论文阅读——Pyramid Grafting Network for One-Stage High Resolution Saliency Detection

目录 基本信息标题目前存在的问题改进网络结构CMGM模块解答为什么要用这两个编码器进行编码 另一个写的好的参考 基本信息 期刊CVPR年份2022论文地址https://arxiv.org/pdf/2204.05041.pdf代码地址https://github.com/iCVTEAM/PGNet 标题 金字塔嫁接网络的一级高分辨率显著性…

虚拟机通过nat模式端口映射实现内网穿透

虚拟机通过nat模式端口映射实现内网穿透 1.网络状态 windows虚拟主机的IP为局域网的私有IP192.168.1.7linux的虚拟主机IP为nat的172.36.4.1062.linux修改nat模式的端口映射 3.windows宿主机防火墙添加规则,&#xff08;或者直接关闭公共网络防火墙&#xff0c;不安全&#xf…

多个excel合并

目的&#xff1a;将同一个文件下的多个 “京东差评.xlsx” 合并为一个&#xff1a;“京东汇总.xlsx" 代码如下&#xff1a; # -*- coding: utf-8 -*- """ Created on Wed Oct 4 12:52:32 2023author: 64884 """import pandas as pd impor…

ffmpeg之去除视频水印

ffmpeg去除水印使用delogo视频滤镜。 delogo参数: x,y,w,h分别表示logo区域的左上角位置及宽度和高度&#xff1b; show:0表示不显示logo区域&#xff0c;1表示显示logo区域。 执行下面的命令&#xff1a; ffmpeg -i 1.mp4 -vf delogox300:y10:w80:h30:show0 out.mp4 效果…

Java数据结构————队列

一 、队列 在Java中&#xff0c;Queue是个接口&#xff0c;底层是通过链表实现的。 只允许在一端进行插入数据操作&#xff0c; 在另一端进行删除数据操作的特殊线性表&#xff0c; 队列具有先进先出FIFO(First In First Out) 。 入队列&#xff1a; 进行插入操作的一端称为…

Android 获取IP地址的Ping值 NetworkPingUtils

项目里需要对动态配置的Ip列表都去ping下延迟&#xff0c;取出其中最小的三个进行随机取值然后去连接&#xff0c;倒腾了一下午终于搞出来了&#xff01; 需求实现思路&#xff1a; 1.找到方法去ping IP地址&#xff1b; 2.同时去Ping&#xff0c;不能让用户等待&#xff1b…

游戏素材网站

OpenGameArt.org&#xff1a;这是一个提供免费游戏素材的社区平台&#xff0c;包括角色、背景、音效、音乐等各种类型的素材。你可以在 https://opengameart.org/ 上找到大量的免费资源。 Kenney.nl&#xff1a;Kenney 是一个知名的游戏开发者&#xff0c;他提供了大量的免费 …

buuctf-[BSidesCF 2020]Had a bad day

打开环境 就两个按钮&#xff0c;随便按按 url变了 还有 像文件包含&#xff0c;使用php伪协议读取一下&#xff0c;但是发现报错&#xff0c;而且有两个.php,可能是自己会加上php后缀 所以把后缀去掉 /index.php?categoryphp://filter/convert.base64-encode/resourcei…

【C++】运算符重载 ⑤ ( 一元运算符重载 | 使用 成员函数 实现 前置 ++ 自增运算符重载 | 使用 成员函数 实现 前置 - - 自减运算符重载 )

文章目录 一、一元运算符重载1、使用 成员函数 实现 前置 自增运算符重载2、使用 成员函数 实现 前置 - - 自减运算符重载 二、完整代码示例 一、一元运算符重载 1、使用 成员函数 实现 前置 自增运算符重载 使用 全局函数 实现 前置 自增运算符重载 : 首先 , 写出函数名 ,…

基于安卓android微信小程序的校园维修平台

项目介绍 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来完成对系统的设计。整…

MIT 6.S081学习笔记(第二章)

〇、前言 本文主要完成MIT 6.S081 实验二&#xff1a;system call 一、Using gdb (easy) Question requirements In many cases, print statements will be sufficient to debug your kernel, but sometimes being able to single step through some assembly code or inspe…

Qt_基础

目录 1概述1.1 什么是QT1.2 QT的发展史1.3 支持的平台1.4 QT版本1.5 下载与安装1.6 QT的优点1.7 成功案例 2 创建 Qt 项目2.1 使用向导创建2.2 .pro文件2.3 帮助文档(QTcreator自带的)2.4 QT应用程序介绍 3 创建第一个小程序3.1 按钮的创建3.1.1 设置主窗口标题的函数3.1.2 **固…

Nuget 安装程序包不成功

1、问题&#xff1a; 安装程序包时&#xff0c;点击安装总是“报错&#xff08;错误1&#xff09;”如下&#xff1a; NU1301 本地源“D:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages”不存在。 LXExamSystem.DAL C:\windowsTest\灵犀考试系统\3、源代…

Is This The Intelligent Model(这是智能模型吗)

Is This The Intelligent Model 这是智能模型吗 Ruoqi Sun Academy of Military Science Defense Innovation Institute, Beijing, 100091, China E-mail: ruoqisun7163.com The exposed models are called artificial intelligent models[1-3]. These models rely on knowled…

微服务技术栈-Gateway服务网关

文章目录 前言一、为什么需要网关二、Spring Cloud Gateway三、断言工厂和过滤器1.断言工厂2.过滤器3.全局过滤器4.过滤器执行顺序 四、跨域问题总结 前言 在之前的文章中我们已经介绍了微服务技术中eureka、nacos、ribbon、Feign这几个组件&#xff0c;接下来将介绍另外一个组…

Maven聚合项目配合Springcloud案例

创建maven项目 导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache…

后端面经学习自测(一)

文章目录 1、MySQL-MVCC2、MySQL-原子性怎么实现3、MySQL-持久性怎么实现隔离性怎么实现 4、操作系统-死锁产生手写死锁死锁排查 5、操作系统-避免死锁死锁的四个必要条件预防死锁 6、操作系统-pageCache是什么零拷贝 7、计算机网络-TCP的可靠性和顺序性怎么实现8、计算机网络-…

【Overload游戏引擎分析】画场景网格的Shader

Overload引擎地址&#xff1a; GitHub - adriengivry/Overload: 3D Game engine with editor 一、栅格绘制基本原理 Overload Editor启动之后&#xff0c;场景视图中有栅格线&#xff0c;这个在很多软件中都有。刚开始我猜测它应该是通过绘制线实现的。阅读代码发现&#xff0…

SpringMVC(二)@RequestMapping注解

我们先新建一个Module。 我们的依赖如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaL…