PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Kleines Script zum Aufräumen

with 16 comments

Ich stand kürzlich vor dem Problem, ein Verzeichnis aufräumen zu müssen, in dem andauernd neue Dateien abgelegt werden, von denen ich aber nur die aktuellsten 5 Dateien behalten möchte.

Gesagt getan.

Erster Schritt ist die Erstellung einer Liste mit allen Dateien um die es geht. Da in dem Verzeichnis noch andere Dateien liegen lasse ich einen regulären Ausdruck entscheiden, ob der Dateiname ins Schema passt oder nicht. Danach wird die Liste der betroffenen Dateien nach dem „Letzten Änderungsdatum“ sortiert und zum Schluss alle überflüssigen Dateien gelöscht. Hier der Quellcode meiner kleinen Klasse:

<?php

class OldFilesCleanup
{
    private $_filenameRegex;
    private $_numberOfFilesToKeep;
    private $_directory;    // with trailing slash

    public function __construct($filenameRegex, $numberOfFilesToKeep, $directory)
    {
        if (substr($directory, -1) != '/') {
            $directory .= '/';
        }

        $this->_filenameRegex       = $filenameRegex;
        $this->_numberOfFilesToKeep = $numberOfFilesToKeep;
        $this->_directory           = $directory;
    }

    public function start()
    {
        $filenames = array();

        $directoryIterator = new DirectoryIterator($this->_directory);
        $regexIterator = new RegexIterator($directoryIterator, $this->_filenameRegex);
        foreach ($regexIterator as $fileinfo) {
            /** @var $fileinfo DirectoryIterator */

            if ($fileinfo->isFile()) {
                $filenames[$fileinfo->getFilename()] = $fileinfo->getMTime();
            }
        }

        arsort($filenames);
        $toBeDeleted = array_slice($filenames, $this->_numberOfFilesToKeep);

        foreach ($toBeDeleted as $filename => $mtime){
            $fullFilePath = $this->_directory . $filename;
            unlink($fullFilePath);
        }
    }
}
$oldFilesCleanup = new OldFilesCleanup('/.*\.tar\.gz$/', 5, __DIR__);
$oldFilesCleanup->start();

In den letzten 2 Zeilen sieht man beispielhaft einen Aufruf mit den Parametern für den regulären Ausdruck, der Anzahl der zu behaltenden Dateien und dem Verzeichnispfad, um den es geht.

Man kann natürlich statt dem DirectoryIterator in Kombination mit dem RegexIterator auch die glob() Funktion zusammen mit filemtime() nutzen können.

Written by Michael Kliewe

Mai 19th, 2011 at 9:52 am

Posted in PHP

Tagged with ,

16 Responses to 'Kleines Script zum Aufräumen'

Subscribe to comments with RSS or TrackBack to 'Kleines Script zum Aufräumen'.

  1. Wäre hier glob nicht irgendwie schöner und schneller? *nur-so-mal-in-die-Menge-frag*

    Oliver

    19 Mai 11 at 15:56

  2. @Oliver: Ein Problem was ich mit glob() habe ist das Problem mit Dateien, die mit einem . anfangen (wie zB .htaccess oder Ordner .svn), die glob() nicht zurückliefert und deshalb „übersehen“ werden. Das kann einem manchmal üble Bugs einhandeln, deshalb nutze ich es wenn es geht nicht mehr.

    Und falls man das ganze rekursiv machen möchte ist die Nutzung vom RecursiveDirectoryIterator irgendwie einfacher als die glob() Funktion recursiv aufzurufen, wobei man dann auch noch auf das open-files-Limit achten muss in das man bei solchen rekursiven glob() Funktionen gern mal reinstolpert.

    Michael Kliewe

    19 Mai 11 at 16:25

  3. glob(‚*‘) ignores all ‚hidden‘ files by default. This means it does not return files that start with a dot (e.g. „.file“).
    If you want to match those files too, you can use „{,.}*“ as the pattern with the GLOB_BRACE flag.

    http://www.php.net/manual/en/function.glob.php#68869

    David Müller

    19 Mai 11 at 16:29

  4. @David: Das ist der Workaround, richtig. Ob das direkt ersichtlich ist was das soll glaube ich nicht, also muss man mit einem Kommentar direkt daneben erklären was dieses ominöse „{,.}*“ zusammen mit GLOB_BRACE da soll. Sorry, aber das ist so ein ekliger Hack, den möchte ich nicht im Code haben wenn es auch einfacher und objektorientiert geht.

    Michael Kliewe

    19 Mai 11 at 16:43

  5. Ich bin halt so ein Fan von möglichst kurzer und einfacher PHP Magie, daher weigere ich mich immer „DirectoryIterator“ zu schreiben. Von versteckten Dateien und rekursiv war ja auch keine Rede. 😀

    Oliver

    19 Mai 11 at 16:51

  6. Da man dieses Script wahrscheinlich in einen Cronjob packen würde, wäre es da nicht sinnvoll auf einem Unix System direkt sowas aufzurufen:

    find /path/to/folder/ mtime +5 -exec rm -f {} \;

    Stefan

    19 Mai 11 at 19:47

  7. @Stefan: Das ist aber kein PHP 😉 Ich glaube vor „mtime“ muß noch ein Minuszeichen oder? Wie kann man das nun noch auf bestimmte Dateien beschränken, beispielsweise mit dem regex von oben: *.tar.gz ?

    Schöne Lösung für das Problem, vielleicht kann es der ein oder andere brauchen.

    Michael Kliewe

    19 Mai 11 at 22:00

  8. @Michael
    ls -t1 /path/ | grep tar.gz$ | tail -n +6 | xargs rm -fr

    Muss ich es erklären?

    Oliver

    19 Mai 11 at 22:35

  9. Trailingslash:
    $directory = rtrim( $directory, '/' ) . '/';
    Finde ich schöner. Ob es schneller ist, müsste mal jemand austesten.

    Ralf

    20 Mai 11 at 03:47

  10. @Oliver
    find hat auch einen -name Parameter:
    find /path/to/folder/ -name „*tar.gz“ -mtime +5 -exec rm -f {} \;

    Aber wenn man sich mal das manual anguckt, sieht man, dass find noch wesentlich mehr kann…

    tjado

    20 Mai 11 at 09:51

  11. @tjado
    Aber find kann nicht sortieren und es sollten ja die letzten 5 Dateien drin bleiben und nicht die Dateien der letzten 5 Tage. 🙂

    Übrigens find kann auch löschen und regexp, dafür kann ls keine kompletten Pfade anzeigen hab ich vorhin gemerkt. Es müsste also so sein:
    cd /path && ls -t1 ./ | grep tar.gz$ | tail -n +6 | xargs rm -fr

    Oliver

    20 Mai 11 at 12:45

  12. Oh stimmt, das mtime +5 tut gar nicht das was ich in diesem Fall möchte.

    @Ralf: finde deine rtrim() Variante irgendwie etwas eleganter als das if mit substr(), Danke dafür

    Michael Kliewe

    20 Mai 11 at 14:05

  13. Braucht DirectoryIterator überhaupt einen trailing slash? Kann ich mir nicht vorstellen..

    nik

    21 Mai 11 at 13:03

  14. @nik: Siehe unlink() Aufruf, außerdem mag ich es wenn ich das Format einer Variablen kenne, gerade bei Pfaden.

    Michael Kliewe

    21 Mai 11 at 13:16

  15. @nik:
    DirectoryIterator vielleicht nicht, dafür aber diese Zeile:
    $fullFilePath = $this->_directory . $filename;
    Spätestens an dieser Stelle müsstest du dafür sorgen das $this->_directory entweder einen trailing slash hat oder nicht.

    Ralf

    21 Mai 11 at 13:18

  16. Aufräumen und Ordnunghaltenisterlernbar. MitdemOrdnungsprofierhalten die Dinge, die Sielieben und braucheneinen fest definiertenPlatz. Miteinfachen und praktischenOrdnungssystemen, findenSie die Dinge in Sekunden und könneneffizientarbeiten und leben. Der OrdnungsprofirichtetIhnen das System ein und Siemüssennurnocheinestun: esbenützen.

    richtig aufräumen

    27 Apr 14 at 11:26

Leave a Reply

You can add images to your comment by clicking here.