Löschen von älteren Backupdateien



  • Guten Tag,

    ich bin dabei ein C++ Programm zu programmieren, welches alte Backupdateien aus einem bestimmten
    Verzeichnis löscht.

    Die Dateien sind alle wiefolgt benannt: "backup.xx.xx.xx"
    Wie lösche ich alle, bis auf, sagen wir, die 10 neusten Dateien?

    Hier gab es einmal eine ähnliche Frage, die war allerdings Unix bezogen.
    Ich programmiere mit Code::Blocks für Windows.

    Ich bedanke mich für jede hilfreiche Antwort.


  • Mod

    Mit welchem Teil davon hast du konkret welche Schwierigkeiten?



  • This post is deleted!


  • Also mir würde dazu ganz spontan ne DateTime-Klasse, regex, filesystem und std::remove einfallen. (ohne jetzt irgendwie weiter zu denken. Das schoss mir nur zuerst in den Kopf)



  • @SeppJ
    Ich weiß nicht, wie ich mit Hilfe der Funktion "GetFileAttributes", welche ich meines Wissens dafür brauche, die jüngesten 10 Dateien feststellen kann.
    Wenns andere Lösungswege gibt, sind diese natürlich auch Willkommen.
    Danke.



  • @deamonwannabee sagte in Löschen von älteren Backupdateien:

    welche ich meines Wissens dafür brauche,

    Warum? Was soll die dir liefern?



  • @deamonwannabee sagte in Löschen von älteren Backupdateien:

    @SeppJ
    Ich weiß nicht, wie ich mit Hilfe der Funktion "GetFileAttributes", welche ich meines Wissens dafür brauche,

    Was meinst du denn, was du davon brauchst?

    Wie möchtest du überhaupt vorgehen?
    Ist nur der Dateiname interessant oder möchtest du nach dem tatsächlichen Erstell-/Änderungs-/Zugriffszeitpunkt gehen?



  • In bash würde ich ja vorschlagen:
    (Edit - SeppJ hat natürlich Recht: nur im konreten Fall der bekannten Dateinamen verwendbar; funktioniert nicht, wenn Dateinamen nicht gutartig sind (Spaces, Zeilenumbrüche, ... im Dateinamen) oder gar keine Datei passt oder Verzeichnisse mit dabei sind)

    ls -t1 | tail -n +11 | xargs rm
    

    Ansonsten in C++: mach dir einen std::vector von struct { filename, time}, sortiere nach time und lösche alle Daten ab Index 10.
    Auflisten: siehe https://en.cppreference.com/w/cpp/filesystem/directory_iterator
    Zeit: siehe z.B. https://en.cppreference.com/w/cpp/filesystem/last_write_time



  • Wenn du das mit der WINAPI lösen möchtest:

    1. FindFirstFile mit entsprechendem Dateipfad und -maske aufrufen ( "c:\data\backups\backup*.*")
    2. absoluten Dateinamen aus Verzeichnisnamen und Treffer aus 1) bilden
    3. CreateFile mit Dateinamen aus 2) zum Lesen aufrufen
    4. Mit dem HANDLE aus 3) GetFileTime() aufrufen
    5. Zeittempel aus 4) zusammen mit dem Dateinamen aus 2) in eine Datenstruktur packen (struct, pair<>) und in einen vector einfügen
    6. mit FindNextFile die nächste Datei bestimmen. Falls es eine gibt: weiter bei 2)
    7. Vektor nach Zeitstempel sortieren
    8. Alle Dateien im Vektor ab Eintrag 10 löschen

    Pseudocode:

    #include <windows.h>
    
    #include <string>
    #include <vector>
    #include <algorithm>
    
    struct FileEntry
    {
        std::string FileName;
        FILETIME CreationTime = 0;
        FILETIME LastAccessTime = 0;
        FILETIME LastWriteTime = 0;
    };
    
    void remove_backups()
    {
       std::string const backup_directory = "c:\\tmp\\backups\\";
       std::string const search_pattern = backup_directory + "backup*.*";
    
       std::vector<FileEntry> file_entries;
    
        WIN32_FIND_DATA wfd;
        HANDLE find_handle = FindFirstFile( search_pattern.c_str(), &wfd );
        if( find_handle != INVALID_HANDLE_VALUE )
        {
           do
           {
               if( !(wfd.wdFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
               {
                   FileEntry fe;
                   fe.FileName = backup_directory + wfd.cFileName;
                   HANDLE file_handle = CreateFile( fe.FileName.c_str(), ... );
                   if( file_handle != INVALID_HANDLE_VALUE )
                   {
                      if( GetFileTime( file_handle, &fe.CreationTime, &fe.LastAccessTime, &fe.LastWriteTime ) )
                      {
                         file_entries.push_back( fe );
                      } 
                      CloseHandle( file_handle );
                   }
               }
           }
           while( FindNextFile( find_handle , &wfd ) ); 
           FindClose( find_handle );
        }
        if( file_entries.size() > 10 )
        {
            std::sort( file_entries.begin(), file_entries.end(), ... );
            for( auto i = file_entries.begin() +10; i < file_entries.end(); ++i )
            {
               DeleteFile( i->FileName.c_str() );
            }
        }
    }
    

  • Mod

    @wob sagte in Löschen von älteren Backupdateien:

    In bash würde ich ja vorschlagen:

    ls -t1 | tail -n +11 | xargs rm
    

    Herzlichen Glückwunsch, du hast den output von ls geparsed und bist prompt in alle üblichen Fallen gestolpert. Was, wenn da Unterverzeichnisse drin sind? Was, wenn es da Dateien mit Leerzeichen oder anderen komischen Zeichen im Namen gibt?

    Stattdessen bitte find mit passenden Einschränkungen nutzen (Oder etwas abenteuerlicher: Nach dem ls die Namen mit / rausfiltern), und beim xargs mit korrekten Placeholders arbeiten.



  • @wob Das Programm zu schreiben ist eine Aufgabe, die mir gegeben wurde. Ergo die Sprache muss c++ sein.


  • Mod

    @deamonwannabee sagte in Löschen von älteren Backupdateien:

    @wob Das Programm zu schreiben ist eine Aufgabe, die mir gegeben wurde. Ergo die Sprache muss c++ sein.

    Fühlst du dich deinen zukünftigen Herausforderungen gewachsen? Wie stellst du dir deine Zukunft vor?



  • @deamonwannabee

    Ich würde mir ganz spontan die Windows-API Funktionen raussuchen, die etwas mit Directorys und Files zu tun haben.

    Eventuell würde ich auch mal eine Suchmaschine bemühen:
    http://www.codebind.com/cpp-tutorial/cpp-program-list-files-directory-windows-linux/

    Das sollte erst einmal einen guten Ausgangspunkt für Dein Programm abgeben.
    Danach könnte ich mich etwas mit den Zeit- und Datumsfunktionen beschäftigen und wie man ein einzelnes Datum vergleicht.

    Zum Schluß würde ich noch rausfinden wollen, wie man eine Datei löscht.



  • #include <cstdlib>
    #include <algorithm>
    #include <vector>
    #include <filesystem>
    
    namespace fs = std::filesystem;
    
    int main()
    {
    	fs::path const directory{ "E:/" };
    	std::vector<fs::directory_entry>::size_type const num_files_to_keep{ 10 };
    
    	std::vector<fs::directory_entry> files;
    	
    	for (auto &entry : fs::directory_iterator{ directory })
    		if (entry.is_regular_file())
    			files.push_back(entry);
    
    	if (files.size() <= num_files_to_keep)
    		return EXIT_SUCCESS;
    
    	std::sort(files.begin(), files.end(), [](fs::directory_entry const &lhs, fs::directory_entry const &rhs)
    		{
    			// return lhs.last_write_time().time_since_epoch() > rhs.last_write_time().time_since_epoch();  // siehe @wob ein Post drunter
    			return lhs.last_write_time() > rhs.last_write_time();
    		}
    	);
    
    	for (auto i{ num_files_to_keep }; i != files.size(); ++i)
    		fs::remove(files[i].path());
    }
    

    🙄



  • @Swordfish sagte in Löschen von älteren Backupdateien:

    return lhs.last_write_time().time_since_epoch() > rhs.last_write_time().time_since_epoch();

    UUOTSE

    (useless use of time since epoch, time_points können direkt verglichen werden)

    (und man könnte überlegen, die Zeiten zu speichern und nicht bei jedem Vergleich neu zu ermitteln)



  • @wob sagte in Löschen von älteren Backupdateien:

    (und man könnte überlegen, die Zeiten zu speichern und nicht bei jedem Vergleich neu zu ermitteln)

    Afaik werden die sowieso nur beim iterieren vom FS gelesen. // edit: jein. Implementation defined. What shalls.


Log in to reply