Erstes Python Programm -- Doppelte Dateien suchen

Code-Stücke können hier veröffentlicht werden.
Antworten
xxy

Hallo zusammen,

bin neu hier im Forum und neu bei python....

Ich habe mir mit dem E-Book "A Byte of Python" (www.byteofpython.info) einen kleinen Einblick in python verschafft.

Mein erstes Übungprogramm sucht in einem Verzeichnis nach doppelten Dateien und gibt diese aus. (Doppelte im Sinn von gleichem Inhalt und nicht gleichem Dateinamen..)

Ich wüsste gerne was ihr dazu meint und hoffe, dass ihr mir ein paar Verbesserungsvorschläge geben könntet.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
#
"""
Dieses Programm sucht nach doppelten Dateien in einem bestimmtem Verzeichnis.

Zum erkennen, ob die Dateien gleich sind, wird md5sum -b benutzt
"""
import os, sys, commands

def search_doublettes(__dirname__):
	'''
	Sucht nach doppelten Dateien in einem Verzeichnis.
	
	Die doppelten Files werden in einem Dictionary zurückgegeben.
	'''

	# Nach Dateien suchen und deren md5 Hash speichern
	print 'Suche nach Dateien im Verzeichnis', __dirname__
	str_path = 'find ' +  __dirname__ + ' -type f -exec md5sum -b \{\} \;'
	lines = commands.getoutput(str_path).split('\n')
	print 'Suche abgeschlossen, starte Vergleich der Dateien.'
	
	datei_dict = {} 	# Enthaelt die untersuchten Dateien
	antwort_dict = {}	# Enthaelt die doppelten Dateien
	
	for line in lines:	
		hash = line.split(' ', 1)
		hash[1] = hash[1].replace('\n', '').replace('*', '') # find fuegt dem Pfad ein * hinzu
		
		# Wenn Hash noch nicht geprueft wurde --> zu datei_dicht hinzu fuegen
		if datei_dict.has_key(hash[0]) == False:
			datei_dict[hash[0]] = hash[1]
		else:
		# Diesen Hash gibt es schon! --> doppelte Datei
			if antwort_dict.has_key(hash[0]):
				antwort_dict[hash[0]].append(hash[1])
			else:
				antwort_dict[hash[0]] = [hash[1], datei_dict[hash[0]]]
	
	return antwort_dict

if __name__ == "__main__":
	# Zu pruefendes Verzeichnis einlesen
	print 'Durchsucht ein Verzeichnis nach Doubletten (gleichen Dateien)\n'
	try:
		if os.path.isdir(sys.argv[1]) == True:
			__dirname__ = str(sys.argv[1])
		else: # Wenn der Parameter kein gültiges Verzeichnis war
			__dirname__ = str(raw_input('Geben Sie das zu durchsuchende Verzeichnis an: '))

	except: # Wenn kein Parameter übergeben wurde
		__dirname__ = str(raw_input('Geben Sie das zu durchsuchende Verzeichnis an: '))

	# Ueberpruefen, ob die Eingabe akzeptabel ist
	if os.path.exists(__dirname__) != True or os.path.isdir(__dirname__) != True:
		print 'Falsches Verzeichnis angegeben'
		sys.exit(1)
	
	# Wenn alles OK ist, Suche starten
	antwort_dict =  search_doublettes(__dirname__)
	
	# Files ausgeben
	if len(antwort_dict) > 0:
		print '\nFolgende Dateien sind gleich:'
		for key, files  in antwort_dict.items():
			print '\nHash:', key
			for datei in files:
				print '\t'+datei
	else:
		print '\nEs wurden keine doppelten Dateien gefunden.'
Und eine Ausgabe des Programms:

Code: Alles auswählen

fri@valhalla ~/Desktop $ ./search_doublettes.py ./
Durchsucht ein Verzeichnis nach Doubletten (gleichen Dateien)

Suche nach Dateien im Verzeichnis ./
Suche abgeschlossen, starte Vergleich der Dateien.

Folgende Dateien sind gleich:

Hash: 3034c746b8510aad871f0ec5dbae7817
        ./test_data/mesmerize_11.wav
        ./test_data/asdfasdf.wav

Hash: a753625175a456f23d56651064ab4a40
        ./test_data/123.MP3
        ./test_data/MANAU__6.MP3

Hash: 88f3f739fae32fbc3a5a3da68f782d94
        ./test_data/mesmerize_3 (Kopie).wav
        ./test_data/mesmerize_3 (noch eine Kopie).wav
        ./test_data/mesmerize_3.wav
gruss xxy
BlackJack

Doppelte Unterstriche vor und nach einem Namen sollten für Python-Interna vorbehalten bleiben. Dein `__dirname__` sollte also besser nur `dirname` heissen.

Am Syntax-Highlighting kann man erkennen, das `hash` der Name einer eingebauten Funktion ist. Diese Namen sollte man nach Möglichkeit nicht für eigene Objekte verwenden.

Du kämst mit einem Dictionary weniger aus, wenn Du einfach erstmal alles ins `antwort_dict` schreibst und da am Ende einfach alle Einträge entfernst wo der Wert die Länge 1 hat.

Einen Wahrheitswert mit `True` oder `False` zu vergleichen ist irgendwie doppelt gemoppelt.

Nie und nimmer nicht ``except`` ohne eine konkrete Ausnahme verwenden. Damit maskiert man sich Fehlermeldungen und wenn man irgendeinen Fehler macht, dann funktioniert das Programm nicht, oder nicht richtig, ohne das man irgendeine Rückmeldung bekommt.

Zuguterletzt die Vorgehensweise: Prüfsummen bilden ist hier nicht die optimale Lösung. So muss jede Datei komplett gelesen werden. Besser ist es erst einmal alle Dateinamen mit der dazugehörigen Dateigrösse zu ermitteln. Diese kann man dann nach Grösse sortieren. Dateien deren Grösse nur einmal vorkommt, braucht man schonmal gar nicht betrachten. Und bei denen mit gleicher Grösse braucht man die auch nur Blöckeweise miteinander vergleichen und kann abbrechen sobald ein Unterschied auftritt. Auf diese Weise muss man nur die Doubletten wirklich komplett lesen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ehm, du nutzt eigentlich fast nur externe Befehle, die es sowieso meist nur unter unixartigen Systemen gibt.

Jetzt mal schnell zusammengehackt, ohne Candy, dafür aber platformunabhängig:

Code: Alles auswählen

#!/usr/bin/env python
# -*- encoding: latin-1 -*-
import os, md5

def main():
    files_here = [entry for entry in os.listdir(os.getcwd()) if os.path.isfile(entry)]
    files_hash = {}
    for path in files_here:
        f = file(path, 'rb')
        sum = md5.new(f.read())
        f.close()
        try:
            files_hash[sum.hexdigest()].append(path)
        except KeyError:
            files_hash[sum.hexdigest()] = [path]
    
    dupes = [values for values in files_hash.values() if len(values) > 1]
    print 'Identical files:'
    print dupes

if __name__ == '__main__':
    main()
Edit (Leonidas): Fährt jetzt auch geradeaus... ähm, ist jetzt auch platformunabhängig.
Zuletzt geändert von Leonidas am Montag 24. Juli 2006, 14:52, insgesamt 1-mal geändert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Leonidas hat geschrieben:

Code: Alles auswählen

        f = file(path, 'r')
Hi Leonidas!

Damit es wirklich plattformunabhängig ist:

Code: Alles auswählen

f = file(path, 'rb')
lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Und wenn ich mir Zeile 9 so anschaue, dann sollte man das nicht auf Verzeichnisse mit CD- oder gar DVD-Images oder Satellitenbilder loslassen. ;-)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:Und wenn ich mir Zeile 9 so anschaue, dann sollte man das nicht auf Verzeichnisse mit CD- oder gar DVD-Images oder Satellitenbilder loslassen. ;-)
Es war nur ein schneller Hack, um das Programm des OP ohne externe Befehle zu implementieren. Für was richtiges würde man erst die Dateigröße checken und Kommandozeilenoptionen hätte es auch verdient. Das war aber im Timeframe alles nicht mehr drin ;)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
xxy

hi zusammen,

vielen dank für die tipps.

ich werde das script einmal anpassen und das ergebnis wieder hier posten..

gruss und danke

xxy
Antworten