【Linux】【C++11】维护指定目录所占空间大小(自动删除文件、文件夹)

2 篇文章 0 订阅

一、前言

实际工程项目中,需要对记录的日志文件大小进行维护,达到一定大小,就要进行删除,以防止把磁盘空间占满。本文主要实现以下两个功能:

  1. 查询指定路径的大小,单位:MB;
  2. 删除指定路径的最旧文件、最旧文件夹。

上述功能主要是通过 Linux 系统中的 POSIX 接口函数实现。

二、代码测试环境

编程语言操作系统系统架构控制器
C++ 11Ubuntu 18.04 LTSarm64LCFC EA-B310

三、代码实现

deleteFiles.cpp

#include <iostream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>

int IsDir(std::string path)
{
    if(path.empty())
	{
		return 0;
	}
	struct stat st;
	if(0 != stat(path.c_str(),&st))
	{
		return 0;
	}
	if(S_ISDIR(st.st_mode))
	{
		return 1;
	}
	else
	{
		return 0;
	}
    return 0;
}


// 获取指定路径的文件大小(递归)
double getFloderSize(std::string &strPath)
{
    double fileSize = 0;
    struct stat t_st;
    if (-1 == stat(strPath.c_str(), &t_st))
    {
        std::cerr << "1 stat() Error: "<< errno << ",Failed to get info of " << strPath << std::endl;   ///< 路径不存在
        return -1;
    }
    
    int nIsDir = IsDir(strPath);
    if((nIsDir) && (strPath.back() != '/'))
	{
		strPath += "/";
	}

    if (!nIsDir)            ///< 如果是文件,返回文件大小
    {
        fileSize = t_st.st_size;
        // printf("111 file size =  %lf\n", fileSize);
        return (fileSize / 1024.0 / 1024.0);
    }
    else
    {
        // 遍历目录下的所有文件夹
        struct dirent* filename = NULL;
        DIR *dir = opendir(strPath.c_str());
        while (filename = readdir(dir))
        {
            std::string filePath = strPath;
            filePath += filename->d_name;

            if(strncmp(filename->d_name, ".", 1) && strncmp(filename->d_name, "..", 2))		///< 避免处理当前目录和上一级目录
            {
                fileSize += getFloderSize(filePath);
            }
        }
        closedir(dir);
    }
    return (fileSize);    
}

/**
 * 获取指定目录下的所有文件夹、文件名
 * @param path 路径(注意:入口参数path只能是目录,不能是文件路径)
 * @return 文件名列表
 */
std::vector<std::string> list_files(std::string &path)
{
    if((IsDir(path)) && (path.back() != '/'))
	{
		path += "/";
	}
    std::vector<std::string> files;
    files.clear();
    DIR *dir = opendir(path.c_str());
    if (dir == nullptr)
    {
        std::cerr << "Error: "<< errno << ",Failed to open directory " << path << std::endl;
        return files;
    }
    dirent *ent = nullptr;
    while ((ent = readdir(dir)))
    {
        if(strncmp(ent->d_name, ".", 1) && strncmp(ent->d_name, "..", 2))		///< 避免处理当前目录和上一级目录
        {
            std::string file_path = path + ent->d_name;
            files.push_back(file_path);
        }
    }
    closedir(dir);
    return files;
}

// 删除非空目录
void removeDirectory(std::string& path) 
{
    DIR* dir = opendir(path.c_str());
    if (dir == nullptr) 
    {
        std::cerr << "Error opening directory: " << path << std::endl;
        return;
    }

    if((IsDir(path)) && (path.back() != '/'))
    {
        path += "/";
    }

    dirent* entry;
    while ((entry = readdir(dir)) != nullptr) 
    {
        if (std::string(entry->d_name) == "." || std::string(entry->d_name) == "..") 
        {
            continue;
        }

        std::string entryPath = path + entry->d_name;
        if (entry->d_type == DT_DIR) 
        {
            removeDirectory(entryPath);
        } 
        else 
        {
            printf("removeDirectory %s\n", entryPath.c_str());
            if (unlink(entryPath.c_str()) != 0) 
            {
                std::cerr << "Error deleting file: " << entryPath << std::endl;
            }
        }
    }

    closedir(dir);

    if (rmdir(path.c_str()) != 0) 
    {
        std::cerr << "Error deleting directory: " << path << std::endl;
    }
}

/**
 * 删除指定路径下最早的文件
 * @param path 路径
 * @return 是否删除成功
 */
bool delete_oldest_file(std::string &path)
{
    if((IsDir(path)) && (path.back() != '/'))
	{
		path += "/";
	}

    // 获取所有文件名
    std::vector<std::string> files = list_files(path);
    if (files.empty())
    {
        std::cerr << "Warning: No regular files found in " << path << std::endl;
        return false;
    }

    // 找到最早的文件
    time_t oldest_time = time(nullptr);
    std::string oldest_file;
    for (const auto &file : files)
    {
        struct stat st;
        if (lstat(file.c_str(), &st) == -1)
        {
            std::cerr << "Error: Failed to get info of " << file << std::endl;
            continue;
        }
        if (st.st_mtime <= oldest_time)
        {
            oldest_time = st.st_mtime;
            oldest_file = file;
        }
    }

    struct stat t_st;
    if (-1 == stat(oldest_file.c_str(), &t_st))
    {
        std::cerr << "delete_oldest_file() stat() Error: " << errno << ", Not find the oldest file " << path << std::endl;
        return -1;
    }

    // 删除最早的文件
    if (!IsDir(oldest_file))    ///< 普通文件直接删除
    {
        if (remove(oldest_file.c_str()) != 0)	///< POSIX接口函数unlink()不支持删除目录,这里使用C++11中的remove函数
        {
            std::cerr << "Error<" << errno << ">: Failed to delete " << oldest_file << std::endl;
            return false;
        }
    }
    else            ///< 如果是目录,递归删除(因为remvoe只能删除空目录,非空目录需要递归删除)
    {
        removeDirectory(oldest_file);
    }
    
    std::cout << "Deleted the oldest file \"" << oldest_file << "\"." << std::endl;
    return true;
}


int main(int argc,char *argv[])
{
	if(argv[1]==NULL)
	{
		printf("Please Input Source Path and Destnation Path\n");
		return 1;
	}
	
	std::string Path=argv[1];//source path

    int i =0;

    double filesSize = getFloderSize(Path);
    std::cout << "文件大小: "<< filesSize << std::endl << std::endl;

    while (200 < getFloderSize(Path))	///< Path路径大于200MB,执行删除程序
    {
        delete_oldest_file(Path);
        sleep(1);       // 每1秒钟检查一次
    }

    // 单独测试 std::vector<std::string> list_files(std::string &path) 函数的健壮性
    // list_files(Path);

    std::cout << "执行完成"<<std::endl;
	
	return 0;
}

编译、运行

g++ -g deleteFiles.cpp -o Delete std=c++11
 
#bash中测试Delete进程
 
./Delete "/home/Zlog/"

四、其它

以上代码是使用C++11编写的,由于C++11没有引入删除非空目录的库函数,所以自定义了一个删除非空目录的函数——void removeDirectory(std::string& path)

但是,在C++17中,引入了filesystem标准库,该库提供了一系列用于处理文件和目录的操作函数。如果你使用的是C++17可以直接调用下面这个库函数进行删除非空目录,而无需自己实现。
std::filesystem::remove_all(const Path& p);

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值