Verzeichnis auf Änderungen überwachen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
ukcdrf
User
Beiträge: 2
Registriert: Mittwoch 4. September 2019, 07:18

Hallo,
ich möchte auf meinem Raspberry Dateien eines Verzeichnisses auf Änderungen überwachen. Dieses Verzeichnis enthält in der Regel zwei PDF-Dateien.
Jede Stunde passiert folgendes: Die beiden PDF Dateien werden zunächst gelöscht und anschließend durch zwei neue PDF ersetzt.
In 95% der Fälle unterscheiden sich die PDF nur durch den Zeitstempel (Dateiname bleibt ebenfalls gleich), in den übrigen 5% wird eine neue modifizierte PDF mit gleichem oder neuem Namen geschrieben.

Nur für diese 5%-Fälle soll jeweils eine Email versendet werden.

Wie löse ich dieses Problem an elegantesten in Python?
Aktuell habe ich dies mit watchdog umgesetzt (Vorlage: https://github.com/ebruAkagunduz/watchd ... itoring.py).

Allerdings versendet dies bei jeder Dateierstellung eine Email und nicht nur bei den 5%.
Bereits der [Create] Vorgang triggert den Email-Versand obwohl die "neuen" Dateien bis auf den Zeitstempel identisch sind (works as designed ;-) )

Frage: Wie überwache ich am geschicktesten solche Dateien/Verzeichnis?

LG
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was du da benutzt ist schon eine gute Wahl. Die gewünschte Logik musst du eben damit umsetzen.
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Also ich finde das (`watchdog`) ist keine gute Wahl weil Cross-Plattform → Es kann nur den kleinsten gemeinsamen Nenner und der ist für diese Aufgabe Sch…. Das funktioniert nur einfach wenn die Dateien in dem Verzeichnis nicht geöffnet, geschrieben, und geschlossen werden, sondern dort hin verschoben werden. Sonst hat man nämlich das Problem, dass es kein Ereignis gibt, das einem sagt, das die Datei komplett geschrieben ist.

Da es sich um einen Raspi handelt, also sehr wahrscheinlich Linux, kann man die `inotify`-Funktionalität vom Kernel verwenden und sich beispielsweise auch über das Schliessen von Dateien informieren lassen. Ist jetzt auch kein Garant das die Datei dann tatsächlich komplett ist, aber es ist recht wahrscheinlich.

Der Code auf Github ist auch nicht so besonders. Python 2, ein Haufen Importe die gar nicht verwendet werden (`git` zum Beispiel, WTF‽), ein komplett sinnfreie `SendMail`-Klasse deren einzige Methode einfach nur eine Funktion sein sollte. Bei `FileEventHandler` sind alle Methoden bis auf die `on_moved()` gleich – das hätte man nicht 3× hinschreiben müssen. Und die `on_moved()` funktioniert nicht, weil da `obj` nie erstellt wird.

``time.asctime(time.localtime(time.time()))`` ist eine sehr umständliche Art um ``time.asctime()`` zu schreiben. Wobei in dem Ausdruck wo das steht auch eine Liste mit *einem* Element erstellt wird und das dann mit ``"".join(…)`` zusammengesetzt wird. Das eine Element ist eine Zeichenkette die aus Literalen und Werten mit ``+`` zusammengesetzt wird.

Das Hauptprogramm erzeugt globale Variablen – das sollte in einer Funktion stecken.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
"""
Copyright (C) 2013 - Ebru Akagündüz <ebru.akagunduz@gmail.com>
This file is part of watchdog-email-notification.
watchdog-email-notification is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
watchdog-email-notification is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
"""
import time
import smtplib
from email.mime.text import MIMEText

from watchdog.events import PatternMatchingEventHandler
from watchdog.observers import Observer

SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SMTP_USERNAME = "user"
SMTP_PASSWORD = "passwd"
EMAIL_TO = "to"
EMAIL_FROM = "from"
EMAIL_SUBJECT = "Info from watchdog"
PATH = "/etc/"


def send_email(file_path):
    message = MIMEText(f"{file_path} changed\n\n{time.asctime()}")
    message["Subject"] = EMAIL_SUBJECT
    message["To"] = EMAIL_TO
    message["From"] = EMAIL_FROM
    mail_server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
    mail_server.starttls()
    mail_server.login(SMTP_USERNAME, SMTP_PASSWORD)
    mail_server.sendmail(EMAIL_FROM, EMAIL_TO, message.as_string())
    mail_server.quit()


class FileEventHandler(PatternMatchingEventHandler):
    @staticmethod
    def _on_event(event):
        send_email(event.src_path)

    on_modified = on_deleted = on_created = _on_event

    @staticmethod
    def on_moved(event):
        # if exist backup file
        if event.src_path + "~" == event.dest_path:
            return
        send_email(event.src_path)


def main():
    observer = Observer()
    event_handler = FileEventHandler(
        ignore_patterns=["*.swp", "*.swx", "*.swpx"]
    )
    observer.schedule(event_handler, PATH, recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()
    

if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
ukcdrf
User
Beiträge: 2
Registriert: Mittwoch 4. September 2019, 07:18

Hi __blackjack__,
vielen Dank für den Vorschlag :-)
Prinzipiell funktioniert dieser, - aber - , genau wie bei der Ausgangslage ist es mir leider nicht möglich folgenden Fall für das Versenden einer Email auszuschließen:
  • Die stündlich neu geschriebene Datei bleibt identisch, lediglich der Zeitstempel ändert sich.
Ich finde leider keine Möglichkeit dies als Trigger auszuschließen, - die Notification-Mails werden stündlich versendet, daher scheint Watchdog möglicherweise nicht der richtige Ansatz zu sein.

Ein alternativer Ansatz könnte wie folgt aussehen:
1. Verzeichnis wird auf Größe in Byte überwacht
2. Erst mit Änderung der Verzeichnisgöße wird eine Email versendet

Das scheint mir mit Watchdog nicht umsetzbar, welche Methodik wäre für so etwas geeignet?

LG
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Unter Distributionen, die systemd einsetzen, kann man das Überwachen alternativ auch mit einer Path Unit lösen: https://www.freedesktop.org/software/sy ... .path.html

Mit deinem Python Script bestimmst du jedenfalls bloß die Hashes der Dateien, vergleichst diese mit denen, die du dir aus dem letzten Lauf generkt hast und sendest eine Mail, wenn sie sich unterscheiden.
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

ukcdrf hat geschrieben: Donnerstag 5. September 2019, 06:07 Das scheint mir mit Watchdog nicht umsetzbar, welche Methodik wäre für so etwas geeignet?
Nezzcarth hat's auch schon angesprochen: einen hash zu bilden (zb mit md5 aus der hashlib) ist die Loesung fuer so etwas. Dabei ist es wichtig, dass du dir die ermittelten Daten Programmlauf-übergreifend merkst (also irgendwo eine Datei speichern kannst).
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

@ukcdrf: Nur weil man das aus den korrekten Antworten falsch interpretieren könnte:
Watchdog sagt dir, dass sich etwas verändert hat. Das kann auch eine identische Datei mit einem neuen Zeitstempel sein.
Ob diese Änderung für dich relevant ist, musst du selbst in deinem Code herausfinden. Die Hinweise wie, stehen in den Beiträgen.
Antworten