Ordnerstrukturen auf Aktualisierung prüfen

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.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Hallo,

ich habe einen USB-Stick E:\ dieser enthält einen Ordner 1, E:\1\
Dieser Ordner 1 enthält dann Unterordner A, B, C, E:\1\B\.
dort sind dann txt-Dateien a, b, c enthalten, welche auch aktualisiert oder hinzugefügt werden, E:\1\B\a.txt

Ich würde gerne ein Programm schreiben, welches alle Unterordner aus 1 durchsucht, ob es neue txt-Dateien gibt. Sollte dies der Fall sein, würde ich die Dateinamen gerne angezeigt bekommen.

Wie gehe ich da am sinnvollsten ran? Einfach E:\1\ prüfen entfällt, da dieser nicht zwangsweise als aktualisiert gekennzeichnet wird. Zumindest wenn ich es über time? lösen würde.

Ich bin für jedes Stichwort oder Idee dankbar :D
Oder ist diese Idee so garnichtumsetzbar?

Danke euch!
ElektroBerry
User
Beiträge: 31
Registriert: Samstag 16. Mai 2020, 18:52

Das einfachste wäre vermutlich den Ordner komplett zu durchsuchen "Path().rglob(".txt")". Und dieses Ergebnis dann zu speichern/vergleichen mit dem letzten Ergebnis.
Und wenn jetzt der Änderungszeitpunkt der Dateien wichtig ist, kann man den ja auch speichern "Path().stat().st_ctime".
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

okay, danke Dir schonmal für den Tipp.

Habe das erstmal so umgesetzt:

Code: Alles auswählen

import os
from pathlib import Path

folder = Path(r'C:\Users\Admin\Documents\Python_Scripts')
file = Path(r'C:\Users\Admin\Documents\Python_Scripts\Hallo1.txt')

#datasize = os.path.getsize(folder)
#print(datasize)

#files = list(folder.rglob('*.txt'))
#print(files)


for file in list(folder.rglob('*.txt')):
    my_match = []
    match = my_match.append(file)

if match != my_match:
    print("neu")
Leider ist das Ergebnis immer "neu", auch wenn ich die Liste my_match vor die Schleife packe.
Da die Suche ja immer aktuell durchgeführt wird, genauso wie das Schreiben der Liste, gibt es ja keine "alte Liste" oder?
Bisher habe ich dazu noch nichts gefunden, ich bin aber mal auf der Suche.
ElektroBerry
User
Beiträge: 31
Registriert: Samstag 16. Mai 2020, 18:52

Code: Alles auswählen

for file in list(folder.rglob('*.txt')):
    my_match = []
    match = my_match.append(file)
Du erstellst eine leere Liste.
Und dann speicherst du die Antwort von der Append-Funktion die ein None zurück gibt, als match.
Wird die Schleife dann nach der letzten Textdatei verlassen. Ist die Liste unzählige male neu erstellt worden, aber enthält immer noch nur die letzte Datei aus der For-Schleife.
Und match ist natürlich None. Deswegen kann match nie my_match sein.

Und folder.rglob() ist eine Generator-Funktion die nicht extra als Liste umgewandelt werden muss, wenn man sie in einem For-Loop benutzt.

Ohne das Benutzen einer Temp-Datei in der die Files aufgelistet sind, könnte das so aussehen:

Code: Alles auswählen

from pathlib import Path
from time import sleep

def main():
    WATCH_DIR = Path('C:/Users/Admin/Documents/Python_Scripts')

    text_files = []
    while True:
        if not text_files:
            text_files = [file for file in WATCH_DIR.rglob("*.txt")]
            print("Erster Scan...")
        else:
            for file in WATCH_DIR.rglob("*.txt"):
                if file not in text_files:
                    print(f"{file} ist neu.")
                text_files.append(file)
        sleep(5)


if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 13063
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ElektroBerry: Das hat aber in bestimmten Situationen ein komisches Verhalten. Wenn beispielsweise am Anfang gar keine Dateien vorhanden sind, dann wird immer wieder "Erster Scan..." ausgegeben auch wenn es gar nicht mehr der erste Scan ist. Den ersten Scan würde man *vor* der Schleife machen.

Und ich würde da statt einer Liste auch eher ein `set()` verwenden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
ElektroBerry
User
Beiträge: 31
Registriert: Samstag 16. Mai 2020, 18:52

Stimmt.
Also mal die Punkte verbessert:

Code: Alles auswählen

from pathlib import Path
from time import sleep


def main():
    WATCH_DIR = Path('C:/Users/Admin/Documents/Python_Scripts')

    print("Erster Scan...")
    text_files = set([file for file in WATCH_DIR.rglob("*.txt")])

    while True:
        for file in WATCH_DIR.rglob("*.txt"):
            if file not in text_files:
                print(f"{file} ist neu.")
            	text_files.add(file)
        if not text_files:
            print("Keine Datei gefunden.")
        sleep(5)


if __name__ == "__main__":
    main()
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@ElektroBerry: um ein Set zu erzeugen braucht man nicht erst eine Liste.

Code: Alles auswählen

text_files = set(file for file in WATCH_DIR.rglob("*.txt"))
Und der Generatorausdruck tut nichts, kann also auch weggelassen werden:

Code: Alles auswählen

text_files = set(WATCH_DIR.rglob("*.txt"))
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Ich danke euch zweien!

Wenn ich die if-Anfrage passend erweitere baue ich noch ein "wurde aktualisert" und "wurde entfernt" ein.

Anschließend versuche ich dann mal die "neuen/aktualisierten" Dateien zu verschieben/kopieren.

Grüße
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Die Probleme mit ganz neu, sowie gelöscht habe ich nun gelöst :)

Nun wollte ich gerne auf Aktualisierung prüfen, dazu wollte ich erstmal die Zeiten der file mit dem des sets vergleichen.
Nunja da gibts den ersten Error
TypeError: stat: path should be string, bytes, os.PathLike or integer, not set

wenn ich das ganze mir als Liste geben lasse, die Vorschläge gab es ja oben auch, erhalte ich selbigen Fehler.
Die Zeit der File kann ich aber doch nicht direkt vergleichen, da die file nur einmal abgefragt wird oder?

Code: Alles auswählen

for file in FOLDER.rglob("*.txt") and text_files:
Hinter der Schleife würde ich dann die passende Zeitabfrage stellen, mMn

Vielleicht übersehe ich aber auch etwas.

Grüße
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Bitte den gesamten Code und die komplette Fehlermeldung angeben.
Der Fehler ist doch offensichtlich. Du versuchst stat mit einem Set aufzurufen, was natürlich keinen Sinn ergibt.
Dass Du os.stat aufrufst, ist eh ein Fehler, da Du mit pathlib arbeitest und daher über die Variable `file` `file.stat()` benutzen solltest.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Code: Alles auswählen

import os
from pathlib import Path
from time import sleep
import time

FOLDER = Path(r')

print("Erster Scan")
text_files = (set(FOLDER.rglob("*.txt")))
i = 0


while True:
    for file in FOLDER.rglob("*.txt"):
        if file not in text_files:  
            print(f"{file} ist neu")
            text_files.add(file)
    for file in text_files:
        if file not in FOLDER.rglob("*.txt"):
            print(f"{file} gelöscht")
            text_files = set(FOLDER.rglob("*.txt"))
    for file in FOLDER.rglob("*.txt") and text_files:    
        
        print(f"{file} aktualisiert")         
    if not text_files:
        print("Keine Daten enthalten")
    i = i + 1
    print("Lauf", i)
    sleep(5)

TypeError: stat: path should be string, bytes, os.PathLike or integer, not set

das ist der aktuelle Code, eigentlich wie oben.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Und wie lautet die komplette Fehlermeldung inklusive Traceback? An welcher Stelle tritt dieser Fehler auf?

Es ist Verschwendung, drei mal pro Durchlauf alle Dateien zu suchen.
Was soll eigentlich ´FOLDER.rglob("*.txt") and text_files` bedeuten? Was wolltest Du damit bewirken?
Wenn man zählen will, benutzt man eine for-Schleife und keine while-Schleife:

Code: Alles auswählen

from pathlib import Path
from time import sleep
from itertools import count

FOLDER = Path('...')

print("Erster Scan")
previous_text_files = set(FOLDER.rglob("*.txt"))

for i in count(1):
    text_files = set(FOLDER.rglob("*.txt"))
    for filename in text_files - previous_text_files:
        print(f"{filename} ist neu")
    for filename in previous_text_files - text_files:
        print(f"{filename} gelöscht")
    previous_text_files = text_files
    print("Lauf", i)
    sleep(5)
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Das sind doch die Tipps, die ihr mir oben geschireben habt :'D
und so wie es da läuft, läuft es doch nicht permanent oder?
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

mit dem dreimal durchsuchen, habe ich das Problem mit dem gelöscht gelöst, da sonst nicht aus der Ausgabe "gelöscht" herauskam.
mit dem ´FOLDER.rglob("*.txt") and text_files` wollte ich den letzten Fall durchgehen. Nämlich wenn eine Datei nur erneuert wird sind diese beiden identisch. Dadurch würde dann danach eine if-Abfrage folgen mit den Zeitstempel-Prüfung
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Was läuft nicht permanent?

Die Tipps habe wir gegeben, aber die Umsetzung ist von Dir, und die Anforderungen ändern sich bei Dir ja auch bei jedem Post.

Wo Du den os.stat-Fehler her hast, hast Du aber immer noch nicht verraten.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

den os.stat-Fehler hatte ich, nachdem ich in meiner Logik das set mit dem stat (Path().stat().st_ctime) als Vorschlag von ElektroBerry kombinieren wollte.
Denn wenn sich die Zeit einer Datei ändert und diese mit dem ersten erstellten set verglichen wird, müsste dort ja die Änderung eintreten (diie Datei wurde aktualisiert?).

Das sich die Änderungen immer ändern liegt ja daran, dass ich das ganze nach und nach aufbaue und immer wieder teste, siehe die Meldung, dass eine Datei gelöscht wurde.
Nun ist die Frage, wenn eine aktuellere Dateio mit selben Namen kommt.

Das ich das ganze in einer while True-Schleife gebaut habe, kam ja nach dem Vorschlag von ElektroBerry. Ob ich den Durchlauf zähle oder nicht, dient hier nur zum erkennen, ob die Schleife wirklich läuft.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Du hast ja schon einen stat-Aufruf von Path. Da macht es natürlich keinen Sinn, damit auch noch os.stat aufzurufen.
Und wie schon geschrieben, wenn man eine endlos-Schleife braucht nimmt man eine while-Schleife, wenn man aber einen Zähler braucht, ist halt eine for-Schleife besser.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Okay, dann ist dies quasi Gesetz mit der while und for-Schleife, obwohl sie für den endlos Fall gleiches tun, danke.

Den stat-Aufruf habe ich ja bei dem set getestet, ohne Erfolg.

Beispeilsweise wäre ja hier nach dann der Fall, was passieren soll, wenn etwas "aktualisiert" wurde.

Code: Alles auswählen

 for filename in previous_text_files and text_files:
demnach würde ich hier hinter eine if-Bedingung mit einer Zeitabfrage der Datei einbauen müssen, weshalb ich wieder mit os.stat für die Datei arbeiten müsste, iwie sowas:

Code: Alles auswählen

os.stat(filename).st_atime
Das funktioniert so aber nicht, da ich keine weitere Abfrage einbaue, erweitere ich die Abfrage zu:

Code: Alles auswählen

if os.stat(filename in previous_text_files).st_atime != os.stat(filename in text_files).st_atime:
            print(f"{filename} aktualisiert")
erhalte ich dauerhaft die Meldung, dass die Datei aktualisiert wurde.

Ich habe das ganze versucht in deinen Code miteinzubauen:

Code: Alles auswählen

from pathlib import Path
from time import sleep
from itertools import count

FOLDER = Path('S:\Palkovits\intern\HPLC-Daten\Jens Heller')

print("Erster Scan")
previous_text_files = set(FOLDER.rglob("*.txt"))

for i in count(1):
    print(os.stat(filename).st_atime)
    text_files = set(FOLDER.rglob("*.txt"))
    for filename in text_files - previous_text_files:
        print(f"{filename} ist neu")
    for filename in previous_text_files - text_files:
        print(f"{filename} gelöscht")
    for filename in previous_text_files and text_files:
        if os.stat(filename in previous_text_files).st_atime != os.stat(filename in text_files).st_atime:
            print(f"{filename} aktualisiert")
    previous_text_files = text_files
    print("Lauf", i)
    sleep(5)
Wo liegt denn mein Denkfehler? oder kenne ich erfahrungsmäßig gewisse Methoden nicht?
Muss ich vllt nach der Abfrage bei gleichen Sets die Datei öffnen und Zeile für Zeile vergleichen lassen?
Falls ja, wie baue ich diese Variante für n Dateien um, was ich bei Goggle fand, war nur zu konkreten Dateien.

Danke für deine Hilfe
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Karlirex hat geschrieben: Mittwoch 31. März 2021, 08:12 Okay, dann ist dies quasi Gesetz mit der while und for-Schleife, obwohl sie für den endlos Fall gleiches tun, danke.
Sie tun eben nicht das selbe. Im einen Fall hast Du einen Zähler, im anderen nicht.
Karlirex hat geschrieben: Mittwoch 31. März 2021, 08:12

Code: Alles auswählen

 for filename in previous_text_files and text_files:
Du kannst nicht Syntax raten und hoffen, dass es schon das macht, was Du hoffst. Hast Du Dir schonmal angeschaut, was der Ausdruck `previous_text_files and text_files` macht??

Genauso `filename in previous_text_files`! Was erwartest Du denn, was dieser Ausdruck innerhalb von os.stat macht? Und hast Du geprüft, was er wirklich macht?

Code baut man Stück für Stück auf, und testet jeden Schritt.
Also Dateien, die früher schon da waren und jetzt immer noch da sind, bekommt man über die Sets:

Code: Alles auswählen

kept_text_files = previous_text_files & text_files
Jetzt hast Du ein Set mit Dateinamen. Aber wie sollst Du daraus die Änderungszeit bestimmen?
Gar nicht. Deshalb mußt Du die Zeiten schon vorher bestimmen. Also neben dem Dateinamen auch die Änderungszeit speichern. Dazu bietet sich ein Wörterbuch an.

Code: Alles auswählen

def get_files_with_atime(folder):
    return {
        filename: filename.stat().st_atime
        for filename in folder.rglob("*.txt")
    }
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Aber den Fall "kept_text_files", welchen du beschreibst, löst du doch genauso über das & (and), wie ich, nur dass du dem ganzen einen neuen Namen (kept_text_files) gibst. Also warum diese Änderung?
Auch wenn while/for etwas anderes darstellen, nach der Konvention, schreibe ich doch nicht 3x Code, nur damit ich dazwischen sehe, ob er wirklich durchläuft, sondern nehme dann nur die letzten Punkte aus der while-Schleife.

Den Fall, welche du mit der Funktion beschrieben hast, gibt doch erstmal den gesamten Ordner als Zeit aus, in einem Wörterbuch gespeichert, oder?

Ich gebe mich mit der ganzen Materie etwas schwer, dass mag sein. Leider komme ich aus keiner Programmierecke und erhalte halt die ein oder andere Idee und versuche mich dann daran. Wenn ich mir verschiedene Bücher dazu ansehe, fühle ich mich leider einfach nur erschlagen, gerade, weil es für eine Lösung eben 7 Wege gibt (siehe unser Schleifenthema).
Antworten