erledigt! wie Doubletten im Dateisystem finden?

Anfängerfragen zum Programmieren mit PureBasic.
EmmJott
Beiträge: 52
Registriert: 25.10.2024 12:23

erledigt! wie Doubletten im Dateisystem finden?

Beitrag von EmmJott »

Guude!

habe eine ganze Menge Dateien hin- und her geschoben, umbenannt, neu verteilt und ... Mist gebaut!

Um das wieder zu restaurieren, muss ich identische Dateien in verschiedenen Unterverzeichnissen finden, wobei ich mich nicht mehr auf den Dateinamen verlassen kann.

Meine Idee war, die Verzeichnisse einzulesen und zu jeder Datei einen Fingerprint zu erzeugen. Habe dafür diese Struktur

Code: Alles auswählen

Structure dircontent  
  filename$
  md5hash$
EndStructure
Das funktioniert soweit. Jetzt habe ich diese Liste "datei.dircontent()" und muss darin die Duplikate finden, also nur die Dateien mit identischem MD5-Fingerprint. Oder mit anderen Worten: Aus der Liste müssen alle Dateinamen verschwinden, deren MD5-Fingerprint nur 1x enthalten ist.

Und genau hier versagt der Denkapparat kläglich. Wer hilft mir auf die Sprünge? And by the way: Die Lösung muss linuxtauglich sein. Würde wegen der "giftigen" Dateinamen die Sache nicht gerne nach Windows verlagern.
Zuletzt geändert von EmmJott am 06.10.2025 07:20, insgesamt 1-mal geändert.
Bin aktiv in der rentenvorbereitenden Arbeitslosigkeit - zwangsweise. Auch nach >30 Jahren im Betrieb springst Du über die Klinge, wenn der (Miss-)Manager seinen Hintern retten will. Lasst Euch von euren Arbeitgebern bloß nix von wegen Loyalität erzählen - wenn's drauf ankommt, ist die nix Wert!
Benutzeravatar
Macros
Beiträge: 1364
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

Re: wie Doubletten im Dateisystem finden?

Beitrag von Macros »

Erste Idee:
Ich würde dafür eine Map (nennen wir sie einfach Counter() ) verwenden.

Wenn du deine Liste erstellst immer bei Counter(md5has$) eins dazu zählen.
Am Ende iteriest du einmal über deine Liste und schmeißt alles raus bei dem Counter(listitem()\md5hash$)=1

KI konnte mir keinen besseren Lösungsweg nennen.

Aber:

Da du Linux erwähnst kannst du dir auch einfach jdupes installieren. Das ist ein fertiges Tool, das genau das macht und zwar unglaublich effizient und flott.
(Besonders wenn du gigantische Dateien hast, da wird dann erst mal nur ein Teil gehasht und erst wenn es eine Datei mit gleichem Anfang und gleicher Größe findet, dann wir weiter verglichen)
Bild
EmmJott
Beiträge: 52
Registriert: 25.10.2024 12:23

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von EmmJott »

Hallo Macros,

vielen Dank für den Tipp. Nach etwas Schlaf und 2 Tassen Kaffee will auch der Denkapparat wieder. Ich bin zufällig in der PB-Hilfe bei

Code: Alles auswählen

 PushListPosition()
auf ein Code-Snippet gestoßen, bei dem Doubletten in einer Liste eliminiert werden. Damit kann man schon mal die Doubletten (Fingerprints) in eine neue Liste kopieren, damit dann in der ursprünglichen Liste alle Dateien suchen, die diese Fingerprints aufweisen. Vielleicht nicht sehr elegant und effizient, tut's aber.
Bin aktiv in der rentenvorbereitenden Arbeitslosigkeit - zwangsweise. Auch nach >30 Jahren im Betrieb springst Du über die Klinge, wenn der (Miss-)Manager seinen Hintern retten will. Lasst Euch von euren Arbeitgebern bloß nix von wegen Loyalität erzählen - wenn's drauf ankommt, ist die nix Wert!
Benutzeravatar
Macros
Beiträge: 1364
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von Macros »

Der Code aus der Hilfe funktioniert dafür auch. Also gut, dass du es damit gelöst hast :)

Ich bin so frech und nutz die Gelegenheit noch für eine kleine Programmierlektion:

Der Nachteil vom Code aus der Hilfe: Er läuft mit O(n²). Was das nun heißt? Nun ja, er schaut sich für jedes Element jedes andere Element an. Die Anzahl der Elemente mit n bezeichnet.
Hier der relevante Ausschnitt:

Code: Alles auswählen

ForEach Numbers()
    Value = Numbers()
    PushListPosition(Numbers())
    While NextElement(Numbers())
      If Numbers() = Value 
        DeleteElement(Numbers())
      EndIf
    Wend
    PopListPosition(Numbers())
  Next
Also du hast die äußere Schleife für jedes Listenelement und die innere für jedes Listenelement ab dem aktuellen. Das ist dann genau genommen n*(n-1)/2 = n²/2-n/2, aber man vereinfacht es in O(n^2), weil man bei der so genannten O-notation nur den am stärksten skalierenden Term, den quadratischen betrachtet.

Was bedeutet das für den Code? Nun wenn du eine Liste mit 10.000 Elementen hineinfütterst muss er grob 10.000² also ca 100.000.000 Operationen ausführen, das dauert!
(Genau genommen sind es 49.995.000, Löschung mal nicht betrachtet)

Die Lösung die ich vorgeschlagen habe hätte z.B. 2 Schleifen über die Liste, aber nicht verschachtelt. Genau genommen sind das 2n Operationen weil zwei Schleifen macht im Beispiel 20.000, oder auch 0,04% der Operationen die man für die verschachtelten Schleifen nutzt. In O Notation ist das O(n), weil man auch Faktoren wie die 2 fallen lässt, für 10.000 Elemente brauchst du also nur ca 10.000 Operationen.

Du merkst, mit der O-Notation kannst du schnell abschätzen, wie effizient ein Algorithmus mit großen Eingabedaten umgeht. Unabhängig davon, wie er dann konkret implementiert wird.


Moderne CPUs sind so flott, da merkst du bei deiner Liste den Unterschied nicht, wenn du nicht gerade zehntausende Dateien hast. Aber bei anderen Dingen kann es sehr nützlich sein, über die Laufzeit des Codes nachzudenken.
Bild
EmmJott
Beiträge: 52
Registriert: 25.10.2024 12:23

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von EmmJott »

Bin sehr dankbar für alle Programmierlektionen, da braucht niemand glauben, er/sie sei frech, wenn ich an seinem/ihrem Wissen partizipieren kann - ich kann da ja nur gewinnen! Und: Man kann halt nur von den Besten lernen!

Mein Progrämmchen funktioniert soweit. Habe mal ein paar Zufallsdateien in Zufallsverzeichnissen erzeugt:
ca. 30000 Dateien (zw. 100 kb und 3 MB groß), insgesamt rund 30 GB
Ich habe jetzt nicht die Schleifendurchgänge zählen lassen (mache ich vielleicht auch noch Interesse halber), aber die meiste Zeit geht für das Fingerprinting drauf, ca. 90 Sekunden. Für das eigentliche Doublettensuchen (ca. 6000 Doubletten im o. g. Beispiel) durch 3 Foreach-Schleifen braucht es gerade mal 5 Sekunden. So gesehen wäre für das Optimieren der Schleifendurchgänge nicht mehr viel zu gewinnen.

Werde jetzt aber doch noch jdupes ausprobieren - will wissen, ob mein Prog auch gründlich arbeitet.
Bin aktiv in der rentenvorbereitenden Arbeitslosigkeit - zwangsweise. Auch nach >30 Jahren im Betrieb springst Du über die Klinge, wenn der (Miss-)Manager seinen Hintern retten will. Lasst Euch von euren Arbeitgebern bloß nix von wegen Loyalität erzählen - wenn's drauf ankommt, ist die nix Wert!
Benutzeravatar
HeX0R
Beiträge: 3044
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von HeX0R »

Hmm...ich hätte das so gemacht:
Also wenn wir von dieser Struktur ausgehen:

Code: Alles auswählen

Structure dircontent  
  filename$
  md5hash$
EndStructure
Dann läufst Du einmal das ganze Verzeichnis durch und ermittelst Name und Hash und fütterst Deine Liste.
Dann Sortierst Du die Liste per SortStructuredList() nach md5hash$ und musst die Liste nur einmal durchgehen.
Einfach immer den aktuellen Hash merken und wenn der nächste identisch ist, hast Du ein doppeltes Lottchen gefunden.
Ich würde evtl. in dem Fall (aus Vorsicht, je nach dem wie wichtig die Daten evtl. sind) auch noch die Dateigröße vergleichen, man weiss ja nie bei diesen Hashes.

Aber klar, die Hasherzeugung braucht eh am längsten.
Du kannst auch einen CRC32 Hash nutzen, das geht schneller, ist aber auch unsicherer.
Spätestens dann würde ich auch die Dateigrößen vergleichen.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8812
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von NicTheQuick »

HeX0R hat geschrieben: 06.10.2025 17:59 Ich würde evtl. in dem Fall (aus Vorsicht, je nach dem wie wichtig die Daten evtl. sind) auch noch die Dateigröße vergleichen, man weiss ja nie bei diesen Hashes.
Solange man kein MD5 oder schlimmeres nimmt, sondern SHA256 oder höher, sind Hashes immer vorzuziehen und es gibt keinen Grund sie nochmal extra zu überprüfen.
HeX0R hat geschrieben: 06.10.2025 17:59 Du kannst auch einen CRC32 Hash nutzen, das geht schneller, ist aber auch unsicherer.
CRC32 sind nicht mehr wirklich schneller heutzutage und sollte man auch definitiv nicht dafür benutzen. Das langsamste ist da eher die Festplatte.

Hier hab ich mal die Zeiten für SHA256, CRC32 und MD5 mit eine 16G großen Datei verglichen auf meinem Rechner:

Code: Alles auswählen

$ time md5sum bigfile.dat
c0db0aad896d9d9fd3c91b7a17fccdb6  bigfile.dat

real	0m22,567s
user	0m16,687s
sys	0m4,897s

$ time crc32 bigfile.dat
75ccf09b

real	0m19,777s
user	0m3,951s
sys	0m6,241s

$ time sha256sum bigfile.dat
f6158d8ddf3516ea1690d9c8fa03a174c081579c11d2405dd62bb927d570d3d2  bigfile.dat

real	0m9,806s
user	0m6,859s
sys	0m2,343s
Wie man sieht ist SHA256 sogar 10 Sekunden schneller als CRC32 und MD5 noch langsamer. Das liegt vermutlich daran, dass diese Verfahren heutzutage in CPUs bereits als Instruktionen vorliegen.

Das ist versteckt in sha_ni integriert:

Code: Alles auswählen

$ lscpu | grep sha_ni
Markierungen:                            fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk clzero irperf xsaveerptr rdpru wbnoinvd arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif v_spec_ctrl umip pku ospke vaes vpclmulqdq rdpid overflow_recov succor smca fsrm debug_swap
jogo
Beiträge: 127
Registriert: 22.11.2020 20:05
Computerausstattung: 'ne Handvoll gebrauchte Laptops & PCs mit Mint und LMDE

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von jogo »

hab hier ein Gemisch aus mehreren Vorschlägen von euch, dazu reicht ein einziger Durchlauf:
Optional: deine Struktur um ein Feld erweitern, falls du für die Dateilöschung eine separate Schleife bauen möchtest:

Code: Alles auswählen

Structure dircontent  
  filename$
  md5hash$
  delflag.b  
EndStructure
1.
du legst zusätzlich zu deiner Dateiliste (wie Macros vorgeschlagen) eine Map an // bleiben wir ruhig bei Counter()

2.
In der gleichen Schleife, mit der du die Dateiliste füllst, checkst du den Hash in Counter():
nachdem du einen Hashwert+Pfad in deine Dateiliste gefüllt hast, suchst du diesen Hash in Counter() mit FindMapElement()

Code: Alles auswählen

If FindMapElement(Counter(), SHA256$)   ;wenn Hash bereits zuvor 1x in Counter() eingetragen wurde.
   ; Hier dann die Routine zum Löschen dieser 2. oder gar 3. Datei im Verzeichnis ausführen
   ; Alternativ/Zusätzlich in deiner Dateiliste \delflag auf 1 setzen (Löschflag)
Else 
    AddMapElement(Counter(), SHA256$) ;diesen (neuen) Hasch hinzufügen (ohne Wertzuweisung, weil nicht nötig)
EndIf
Das wars. Wenn deine Schleife für Dateiliste durch ist, sind auch alle Doubletten gelöscht bzw. Alternativ sind diese Doubletten in der Dateiliste \delflag zum Löschen markiert.

PS:
Den Vorschlag von HeX0R finde ich auch Charmant. Müßte man jetzt mal nachmessen, wie lange die Sortierung von ca. 30.000 Einträgen dauern würde...
--
Ideen gibt es viele - man muss sie nur haben...
Mint / LMDE5+6 // PureBasic 6.21
Benutzeravatar
HeX0R
Beiträge: 3044
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von HeX0R »

NicTheQuick hat geschrieben: 06.10.2025 19:23 CRC32 sind nicht mehr wirklich schneller heutzutage und sollte man auch definitiv nicht dafür benutzen. Das langsamste ist da eher die Festplatte.
Tatsache :shock:
Da hat sich wohl das alte Denken bei mir eingebrannt, hätte ich jetzt so nicht erwartet.
jogo hat geschrieben: 06.10.2025 21:14PS:
Den Vorschlag von HeX0R finde ich auch Charmant. Müßte man jetzt mal nachmessen, wie lange die Sortierung von ca. 30.000 Einträgen dauern würde...
Du wirst vermutlich erstaunt sein, die Sort-Befehle in PB waren bei mir schon öfter ein Gamechanger und sind i.d.R. pfeilschnell.
EmmJott
Beiträge: 52
Registriert: 25.10.2024 12:23

Re: erledigt! wie Doubletten im Dateisystem finden?

Beitrag von EmmJott »

Sagenhaft! Freue mich über jeden Beitrag hier, da gibt's viel zu lernen! Vielen Dank an Euch alle!
Es ging nicht darum, gefundene Doubletten zu löschen. Das wäre tatsächlich in einem Rutsch zu erledigen. Ich musste prüfen, welche Dateien ggf. auch mehrere Doppelgänger haben, da nicht klar ist, welche Datei davon "das Original" war (richtiger Dateiname, richtiger Speicherort, richtiger Inhalt). Ich brauchte also eine Liste der Dateien, deren Fingerprint kein Unikat war (das Aussortieren war dann Handarbeit).

Mit eurem Input hier würde ich es jetzt so machen:
Structure dircontent um ein Feld "doublette" erweitern
Verzeichnisse einlesen, SHA256-Fingerprint erstellen, Feld "doublette" = false
Liste aufsteigend nach Fingerprint sortieren
Durchlaufen, ob ein Fingerprint doppelt ist, wenn ja, Feld "doublette" = true
Damit würde jedoch der erste Doubletten-Fingerprint nicht gekennzeichnet werden. Deswegen:
Liste absteigend nach Fingerprint sortieren und 2. Durchlauf, ob ein Fingerprint doppelt ist, wenn ja, Feld "doublette" = true
Dann wären alle "Nicht-Unikate" gekennzeichnet.

Macros Tipp, ein bereits für Linux bestehendes Tool zu verwenden, hätte mich beinahe um den ganzen Programmier-Spaß gebracht. Habe "fdupes" gefunden (hat er wohl auch gemeint) und das ist rasend schnell und bietet eine ganze Menge an Parametern für's Feintuning.
Bin aktiv in der rentenvorbereitenden Arbeitslosigkeit - zwangsweise. Auch nach >30 Jahren im Betrieb springst Du über die Klinge, wenn der (Miss-)Manager seinen Hintern retten will. Lasst Euch von euren Arbeitgebern bloß nix von wegen Loyalität erzählen - wenn's drauf ankommt, ist die nix Wert!
Antworten