一、前言
实际工程项目中,需要对记录的日志文件大小进行维护,达到一定大小,就要进行删除,以防止把磁盘空间占满。本文主要实现以下两个功能:
- 查询指定路径的大小,单位:MB;
- 删除指定路径的最旧文件、最旧文件夹。
上述功能主要是通过 Linux 系统中的 POSIX 接口函数实现。
二、代码测试环境
编程语言 | 操作系统 | 系统架构 | 控制器 |
---|---|---|---|
C++ 11 | Ubuntu 18.04 LTS | arm64 | LCFC 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);