Open Source im professionellen Einsatz
Linux-Magazin 09/2005

Desktop-Suche mit Perl-Skript

Persönlicher Spürhund

Das Kommandozeilentool »find« hangelt sich nur sehr gemächlich durchs Dateisystem. Dagegen lokalisiert das hier vorgestellte Skript »rummage« dank einer Datenbank mit Meta-Informationen blitzschnell selbst entlegene Dateien.

1183

Wo ist das Skript, das ich gestern zusammenklopft habe? Welche Dateien sind neu, brauchen den meisten Platz oder wurden seit drei Jahren nicht mehr angefasst? Dateisysteme lassen sich natürlich schrittweise durchforsten und so die Antworten auf solche Fragen finden. Spottbillige Riesenfestplatten haben allerdings in den letzten Jahren dafür gesorgt, dass Anwender ihr Homeverzeichnis nicht regelmäßig ausmisten und Find & Co. oft zehn- oder gar hunderttausend unnütze Einträge abwandern müssen. Das dauert.

Utilities wie »slocate« durchforsten den Dateibaum bei Nacht, legen dabei einen Index an und erlauben es dem Anwender, die indizierten Dateien anschließend schnell über ihren Namen zu finden. Der Google-Desktop [2] und Spotlight unter Mac OS X setzen sogar noch eins drauf: Sie indizieren zusätzlich verschiedene Metadaten und gestatten es dem Benutzer damit, Dateien anhand von Merkmalen wie Typ, Größe, Name oder Alter wiederzufinden.

Das hier vorgestellte Skript »rummage« implementiert eine Desk-topsuche in Perl. Es achtet nicht nur auf Dateinamen, es merkt sich au-ßerdem, wann Dateien zum ersten Mal auftauchten und wann sie zum letzten Mal geändert wurden. Es legt eine Reihe von Meta-Informationen über jede gefundene Datei in einer MySQL-Datenbank ab (Abbildung 1). Von Textdateien bildet es einen Volltextindex, damit der Benutzer später per Keyword-Suche ihren Inhalt durchstöbern kann.

Volltext auf Abwegen

Seit der Version 3.23.23 bietet MySQL eine Option, die Tabellenspalten markiert, deren Inhalt später per Volltextsuche zugänglich sein soll. Mit 4.0.1 kamen boolesche Verknüpfungen der Suchausdrücke hinzu. Der Anwender kann sogar Stopplisten anlegen, mit denen er häufige, aber nicht sinntragende Wörter von der Indizierung ausschließt. Außerdem beherrscht die Datenbank Query-Expansion, kann also in Dokumenten suchen, die sie ihrerseits erst im Ergebnis einer Abfrage fand. Allerdings lässt die Query-Geschwindigkeit bei vielen Dateien stark zu wünschen übrig. Und da jedes Volltextdokument in die Datenbank wandert, nimmt diese bald gigantische Ausmaße an.

Auch das Perl-Modul »DBIx::FullTextSearch«, das einen eigenen Index definiert und MySQL als Backend verwendet, hat Macken. Die Indizierung ist recht langsam und sobald mehr als 30000 Dateien im Index sind, nimmt die Geschwindigkeit überproportional ab. Rummage setzt daher auf den bewährten Indizierer Swish-E [3], der rasend schnell arbeitet, nach Schlüsselworten und Phrasen sucht und problemlos skaliert.

Das Modul »SWISH::API::Common« vom CPAN erleichtert die Kommunikation mit Swish-E, indem es sich auf die am häufigsten genutzten Aspekte konzentriert. Allerdings kann Swish-E keine Dateien aus einem einmal erstellten Index löschen, sodass man am besten jeden Tag alles neu indiziert. Während eines nächtlichen Cronjobs schafft der Indizierer problemlos ein paar hunderttausend Dateien, das dürfte für den Hausgebrauch reichen. Zum Beispiel: »05 03 * * * LD_LIBRARY_PATH=/usr/local/lib /home/mschilli/bin/rummage -u -.v /dev/null 2>&1«

Nach dem ersten Indizierungslauf mit »rummage -u« (für Update) darf der Benutzer endlich auf die Metadaten und den Volltextindex zugreifen. Textdateien, die ein Schlüsselwort enthalten, zeigt der Befehl »rummage -k query« an (»-k« steht für Keyword). Der Kasten "Rummage-Kommandos" enthält Beispiele. Wie das Schema in Abbildung 1 zeigt, speichert die MySQL-Datenbank zu jeder Datei den vollständigen Pfad, die Größe in Bytes und die Zeitpunkte ihres ersten Auftauchens, des letzten Lesezugriffs und der letzten Modifikation.

Eine Datei »call.sgml«, die sich irgendwo versteckt in den Tiefen der indizierten Hierarchie befindet, ist per Pfad-Match mit »rummage -p call.sgml« zu finden. Aus »call.sgml« macht »rummage« intern das SQL-Pattern »%call.sgml%« und fragt die Tabelle »file« mit »WHERE path LIKE "%call.sgml%"« ab. Auch ein Teilpfad wie »Beispiele/call.sgml« funktioniert, dann entdeckt »rummage« die Datei nur, falls sie in einem Unterverzeichnis namens »Beispiele« liegt.

Die letzten 20 Dateien, die kürzlich modifiziert wurden, findet der Aufruf »rummage -n 20«. Ohne Angabe eines Integers kommen die Pfade der letzten zehn modifizierten Dateien zurück. Alle innerhalb der letzten Woche geänderten Dateien, zeigt der Aufruf »rummage -m \'7 day\'«. Er generiert eine MySQL-Abfrage der Form:

SELECT * FROM file WHERE DATE_SUB(NOW(), INTERVAL 7 DAY) <= mtime

MySQL rechnet hier für jeden Eintrag aus, ob das Änderungsdatum länger als sieben Tage zurückliegt. Bei Bedarf lässt sich die Anzahl Tage in dem Ausdruck auch gegen »3 month« oder »18 hour« austauschen. Das alles geht natürlich nicht in Echtzeit, sondern bezieht sich auf das letzte Update der Datenbank in der Nacht zuvor. Alles was danach geschah, ist für »rummage« unsichtbar.

Der erste Abschnitt in Listing 1 sollte an die individuellen Bedürfnisse angepasst werden. Die Konstante »$MAX_SIZE« bestimmt die maximale Länge des indizierten Inhalts einer Textdatei. So lässt sich beispielsweise von Logfiles nur der Anfang im Index berücksichtigen. Der Wert »100000« bestimmt in diesem Beispiel, dass das Tool nur die ersten 100 KByte indiziert.

Eine Zeile weiter nennt der Data Source Name »$DSN« dem DBI-Class-Modul den Datenbanktreiber und den Namen der Datenbank (»dts«). Schließlich ist »@DIRS« ein Array von Verzeichnisnamen, die »rummage« rekursiv durchstöbert. Gibt der Benutzer statt Verzeichnissen symbolische Links an, löst Zeile 17 diese auf. Wenn das Indizieren des Homeverzeichnisses zu lange dauert, ist die Beschränkung auf ein oder mehrere Unterverzeichnisse möglich, etwa auf einen lokalen CVS-Workspace.

Rummage-Kommandos

Datenbank auffrischen oder generieren:rummage -u -v Schlüsselwortsuche nach »linux«:rummage -k \'linux\' Phrasensuche: rummage -k \'"mike schilli"\' »foo« und »bar« oder »baz« finden:rummage -k \'foo AND (bar OR baz)\' Wildcard-Suche: rummage -k \'torvald*\' Datei per Name oder Pfad aufstöbern:rummage -p Teilpfad Die letzten 20 modifizierten Dateien:rummage -n 20 Vergangene Woche modifizierte Dateien:rummage -m \'7 day\'

Unverzichtbarer Prototyp

Die Zeile 19 deklariert die Funktion »psearch()«, die später Suchergebnisse der verschiedenen Abfragen ausgibt. Dies geschieht mit Hilfe eines Prototyps, der festlegt, dass »psearch()« den ersten und einzigen Parameter als Skalar erwartet. Das ist wichtig, denn die Ausgabe der DBI::Class-Methoden »search()« oder »search_like()« an »psearch()« sollen in skalarem Kontext stehen, weil sie nur so einen Iterator zurückgeben, den »psearch()« auswertet.

Fiele der Prototyp weg, stünde die Methode »search()« in dem Ausdruck »psearch($db->search(...))« im Array-Kontext ­ und damit gäbe die »search()«-Methode des Moduls DBI::Class nach ihrer Definition eine Liste von Treffern zurück und keinen Iterator.

»getopts()« analysiert die übergebenen Kommandozeilenparameter. Falls sich herausstellt, dass mit »-u« ein Update der Datenbank gefordert ist, schaltet Zeile 23 das Log4perl-Framework ein. Wurden mit »-v« (verbose) ausführliche Meldungen verlangt, wird der Level auf »$DEBUG« gesetzt, sonst kommen mit »$INFO« nur wichtige Meldungen durch. Die Logdatei wird mit jedem Lauf neu überschrieben, damit sie nicht auf Dauer die Platte füllt. Eine Alternative wäre eine Log4perl-Konfiguration mit »Log::Dispatch::FileRotate«.

In Zeile 29 ruft »db_init()« die gleichnamige Funktion in Zeile 144 auf, die die Datenbank mit der Tabelle »file« initialisiert, sofern noch nicht geschehen. Außerdem definiert sie einen Index auf die Spalte »path«, damit »rummage« später blitzschnell nachsehen kann, ob ein Eintrag für eine Datei schon existiert oder nicht und ob sich der Zeitstempel geändert hat. So kann der erste Suchlauf von »rummage« nach der Installation einige Zeit dauern. Darauf folgende Auffrischläufe gehen deutlich schneller.

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Finden im Sauseschritt

    Eine Website mit einer Suchfunktion versehen ist Stand der Technik. Um aber die eigene Festplatte umfassend und mit all den Dateiformaten abseits von HTML zu durchsuchen, braucht es etwas Effizienteres als Grep oder einen einfachen Index: Swish-E.

  • Ordnung im Chaos

    Jeden Tag plagen sich Computernutzer mit der wachsenden Menge an Daten herum: E-Mails, Chat-Protokolle, Office-Dokumente, Urlaubsfotos, Musikdateien. Desktop-Suchmaschinen treten an, um in dem Chaos die gesuchten Informationen zu finden. Dieser Artikel zeigt, ob sie es schaffen.

  • Versteckspiele

    Bei Volltext-Suchmaschinen für Webseiten ist die Auswahl groß. Wer aber eine Standalone-Lösung für die Files auf der eigenen Festplatte will oder eine CD-ROM mit Volltextindex plant, muss länger nach geeigneten Tools fahnden. Findige Programmierer entwickeln kurzerhand eigene Such-Applikationen.

  • Hilfe bei der Spurensuche

    Eindringlinge an ihren Spuren erkennen - das ist das Ziel des Advanced Intrusion Detection Environment. In einer Datenbank hinterlegt AIDE die Attribute und kryptographischen Prüfsummen wichtiger Files und Verzeichnisse. So bemerkt es jede nicht autorisierte Änderung und schlägt Alarm.

  • Perl-Snapshot

    Passend zum Titelthema: Mike
    Schilli nutzt Mouse und Moose, um
    Distributionsrepositories abzufragen.

comments powered by Disqus

Ausgabe 06/2017

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.