delete files older than n

What is the best way to delete all files that haven't been modified in X days?

Thank you.
"best" is subjective. This is one of them: https://linux.die.net/man/1/find
> What is the best way to delete all files that haven't been modified in X days?

A portable solution is usually better.

std::experimental::filesystem (TS, C++14) / std::filesysten (C++17)
http://en.cppreference.com/w/cpp/filesystem
or boost::filesystem http://www.boost.org/doc/libs/1_63_0/libs/filesystem/doc/index.htm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64

#include <iostream>
#include <string>
#include <experimental/filesystem>
#include <chrono>
#include <vector>

namespace fs = std::experimental::filesystem ;

bool is_older_than( const fs::path& path, int hrs )
{
    auto now = fs::file_time_type::clock::now() ;
    using namespace std::chrono ;
    return duration_cast<hours>( now - fs::last_write_time(path) ).count() > hrs ;
}

std::vector<fs::path> files_older_than( fs::path dir, int hrs )
{
    std::vector<fs::path> result ;

    for( const auto& p : fs::recursive_directory_iterator(dir) )
        if( fs::is_regular_file(p) && is_older_than( p, hrs ) ) result.push_back(p) ;

    return result ;
}

std::pair<int,bool> remove_files_older_than( fs::path dir, unsigned int days )
{
    int cnt = 0 ;
    try
    {
        for( const auto& p : files_older_than( dir, days*24 ) )
        {
            fs::remove(p) ;
            ++cnt ;
        }
        return { cnt, true };
    }
    catch( const std::exception& ) { return { cnt, false } ; }
}

http://rextester.com/JFCT22957
You can also checkout Kahless_9's delete_older utility at http://www.shankodev.com/Tools/items/delete_older/delete_older_Page_00.htm

Apparently this is a task that is commonly required on backend servers where work related process and log files may accumulate, hence the exact utility delete_older is provided for among some other usefull ones.

The source code for this utility is provided and can be obtained from the frameworks [Tools\cleanup_utils\xxx_older\delete_older].

The crux of this utilities code is as follows:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

void CConsoleMain::Process()
{
   KDirTraverser dirTrav;
   dirTrav.SetFileSpec(m_strFileSpec);
   dirTrav.SetRecurtSubDirs(m_bProcessSubDirs);
   dirTrav.SetFileExecInfoFunc(_ProcessFileFunc, this);
   dirTrav.SetPreDirChangeInfoFunc(_ProcessEnterSubDirFunc, this);
   dirTrav.SetPostDirChangeInfoFunc(_ProcessLeaveSubDirFunc, this);
   dirTrav.Execute();
}


void CConsoleMain::_ProcessFileFunc(const KStr & strDir, const _finddata_t & fileinfo, LPVOID pParam)
{
   CConsoleMain * inst = (CConsoleMain *)pParam;
   KDate dtFileLastMod = KDate(fileinfo.time_write).GetDate();
   if (inst->m_dtDeleteOlderThanDate > dtFileLastMod)
   {
      int nResult = _unlink((LPCTSTR)(strDir+"\\"+fileinfo.name));
      if (nResult)
      {
         printf("unable to delete file [%s] due to [%s]\n", fileinfo.name, strerror(errno));
         return;
      }
      printf("deleted file [%s]\n", fileinfo.name);
   }
}



The linux version is slightly different in that it does not invoke a callback function that accepts a _finddata_t structure as this is windows specific.

Instead the linux version invokes the callback function that passes the filename as a string. The callback function then uses this filename to retrieve its file times via a call to KFileIO::GetFileInfo

The linux and windows version could both have been made exacty like the linux version but I suppose the windows version was developed to take advantage of the extra features provided from the windows version of the framework.

One thing I've also noticed when using [delete_older] on a server is that you may afterward end up with a number of empty folders.

To clear these you could use the frameworks [erase_empty] utility for which you should also be able to get the source code for.
Last edited on
Thank you. It needs to be portable.

I am a beginner.

I assume a system call is a bad choice right?

Find in Linux and forfiles in Windows:
system(forfiles -p "C:\what\ever" -s -m *.* -d <number of days> -c "cmd /c del @path")

This is for a temp folder. Some files in the temp folder will be reused but they will also consume the hard drive if left unchecked.

I would like to check if folder is larger than 20% of total free space, delete files from folder, starting with oldest modified date first, until folder is less than 20% free space.

Is there a simple way for that?

Would comparing the folder size after each individually deleted file be intensive? If so, would this be a better solution?

1
2
3
4
5
6
7
8
if (folder is too large)
  delete files older than 3 months
if (folder is too large)
  delete files older than 2 months
if (folder is too large)
  delete files older than 1 month
if (folder is too large)
  delete files older than 1 day
Something like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <iostream>
#include <string>
#include <experimental/filesystem>
#include <cstdint>
#include <chrono>
#include <vector>
#include <algorithm>

namespace fs = std::experimental::filesystem ;

std::uintmax_t space_to_be_freed( const fs::path& dir, unsigned int percent_free_required )
{
    const fs::space_info space_info = fs::space(dir) ;
    std::uintmax_t required = space_info.capacity * percent_free_required / 100.0 ;
    return space_info.available < required ? required - space_info.available : 0 ;
}

struct file_info
{
    fs::path path ;
    fs::file_time_type last_write_time ;
    std::uintmax_t size ;
};

std::vector<file_info> file_list( const fs::path& dir )
{
    std::vector<file_info> result ;

    for( const auto& p : fs::recursive_directory_iterator(dir) )
    {
        const auto& path = p.path() ;
        if( fs::is_regular_file(path) )
            result.push_back( file_info { path, fs::last_write_time( path ), fs::file_size( path ) } ) ;
    }

    return result ;
}

// return the number of bytes freed
std::uintmax_t remove_files( const fs::path& dir, std::uintmax_t space_to_be_freed )
{
    std::uintmax_t space_freed = 0 ;

    try
    {
        auto flist = file_list(dir) ;

        // sort the list with the older files before the newer files
        static const auto cmp_times = [] ( const file_info& a, const file_info& b ) 
        { return a.last_write_time < b.last_write_time ; } ;
        std::sort( std::begin( flist ), std::end( flist ), cmp_times ) ;
        
        // free files one by one till target is reached
        for( std::size_t i = 0 ; i < flist.size() && space_freed < space_to_be_freed ; ++i )
        {
            fs::remove( flist[i].path ) ;
            space_freed += flist[i].size ;
        }
    }

    catch( const std::exception& ) {}

    return space_freed ;
}

int main()
{
    const auto required = space_to_be_freed( "C:/", 30 ) ; // 30 percent free space 
    std::cout << "required to be freed: " << required << '\n' ;

    if( required > 0 )
    {
        const auto result = remove_files( "C:/Windows/Temp", required ) ;
        std::cout << result << " bytes were freed\n" ;
    }
}

http://rextester.com/LYXRA64594
Topic archived. No new replies allowed.