archivosDuplicados.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #!/usr/bin/python3
  2. '''
  3. Buscador de archivos duplicados en un árbol de directorios
  4. Copyright (C) 2018 Rodrigo Garcia <strysg@riseup.net>
  5. '''
  6. import hashlib
  7. import os
  8. import sys
  9. # helpers
  10. def getList(directory="."):
  11. ''' retorna una lista con los nombres de todos los archivos dentro el
  12. directorio actual.
  13. * basado en https://stackoverflow.com/questions/120656/directory-tree-listing-in-python#120701
  14. '''
  15. files = []
  16. for dirname, dirnames, filenames in os.walk(directory):
  17. # for subdirname in dirnames:
  18. # files.append(os.apth.join(dirname, subdirname))
  19. for filename in filenames:
  20. files.append(os.path.join(dirname,filename))
  21. return files
  22. def digest(filename, algorithm='sha1'):
  23. ''' returns hexdigest of the given filename using the gibe algorithm '''
  24. with open(filename, 'r+b') as fil:
  25. if (algorithm == 'sha1'):
  26. return hashlib.sha1(fil.read()).hexdigest()
  27. elif (algorithm == 'sha224'):
  28. return hashlib.sha224(fil.read()).hexdigest()
  29. elif (algorithm == 'sha256'):
  30. return hashlib.sha256(fil.read()).hexdigest()
  31. elif (algorithm == 'sha384'):
  32. return hashlib.sha384(fil.read()).hexdigest()
  33. elif (algorithm == 'sha512'):
  34. return hashlib.sha512(fil.read()).hexdigest()
  35. elif (algorithm == 'md5'):
  36. return hashlib.md5(fil.read()).hexdigest()
  37. print ('Invalid algorithm')
  38. return ""
  39. def digests(fileList, algorithm='sha1'):
  40. ''' Retorna un diccionario con los digestos calculados de la lista de
  41. archivos `fileList'.
  42. '''
  43. dict = {}
  44. for file in fileList:
  45. d = digest(file)
  46. if d in dict:
  47. l = dict[d]
  48. l.append(file)
  49. dict[d] = l
  50. for file in fileList:
  51. d = digest(file)
  52. if d in dict:
  53. # duplicado encontrado
  54. l = dict[d]
  55. l.append(file)
  56. dict[d] = l
  57. else:
  58. l = []
  59. l.append(file)
  60. dict[d] = l
  61. return dict
  62. def eliminarArchivo(archivo):
  63. ''' elimina el archivo con ruta absoluta
  64. -- return: True si lo logra.
  65. '''
  66. if os.path.exists(archivo):
  67. os.remove(archivo)
  68. return True
  69. else:
  70. print('El archivo no existe:', archivo)
  71. return False
  72. def crearEnlaceSimbolico(fuente, destino):
  73. ''' crea un enlace simbolico que hace que `destino' apunte a `fuente'
  74. -- return: True si lo logra.
  75. '''
  76. if not os.path.exists(fuente):
  77. print ('El archivo no existe:', fuente)
  78. return False
  79. if not os.path.exists(destino):
  80. print ('El archivo no existe:', destino)
  81. False
  82. try:
  83. os.symlink(fuente, destino)
  84. print ('creado enlace simbolico')
  85. return True
  86. except ex:
  87. print ('Excepcion generada:', str(ex))
  88. return False
  89. def eliminarYCrearEnlaceSimbolico(fuente, destino):
  90. ''' Elimina el archivo `destino' y en el mismo directorio crea un enlace simbolico
  91. que apunta al archivo `fuente'.
  92. -- return: True si lo logra.
  93. '''
  94. if not eliminarArchivo(destino):
  95. return False
  96. if not crearEnlaceSimbolico(fuente, destino):
  97. return False
  98. return True
  99. def use():
  100. print ('Obtiene un lista de archivos duplicados desde un directorio raíz')
  101. print ('Cada linea contiene los archivos que se ha detectado iguales')
  102. print ()
  103. print (' python3 duplicados.py [opcion] [DIR] [ALGORITMO]')
  104. print ()
  105. print (' - DIR: Directorio raíz donde realizar la búsqueda usa "." por defecto')
  106. print (' - ALGORITMO: Algoritmo para obtener digestos "sha1" por defecto')
  107. print (' permitidos; md5,sha1,sha224,sha256,sha384,sha512')
  108. print (' opcion:')
  109. print (' d: Elimina los archivos duplicados dejando solo un archivo fuente.')
  110. print (' s: Elimina los archivos duplicados y crea un enlace simbolico hacia el archivo fuente en lugar de los archivos eliminados.')
  111. print ('EJEMPLO')
  112. print (' python3 duplicados.py /tmp/dir1')
  113. print (' python3 duplicados.py -d /tmp/dir1')
  114. print (' python3 duplicados.py -s /tmp/dir1')
  115. # main
  116. files = []
  117. directory='.'
  118. hashAlgorithm = "sha1"
  119. eliminar_duplicados = False
  120. crear_enlaces_simbolicos = False
  121. if (len(sys.argv) > 1):
  122. if (sys.argv[1] != ''):
  123. if (sys.argv[1]=='-h' or sys.argv[1]=='--help'):
  124. use()
  125. exit(0)
  126. if (sys.argv[1]=='-d'):
  127. eliminar_duplicados = True
  128. directory=sys.argv[2]
  129. elif (sys.argv[1]=='-s'):
  130. crear_enlaces_simbolicos = True
  131. directory=sys.argv[2]
  132. else:
  133. directory=sys.argv[1]
  134. if (len(sys.argv) > 3):
  135. if (eliminar_duplicados or crear_enlaces_simbolicos and sys.argv[3] != ''):
  136. if (sys.argv[3]=='sha1' or sys.argv[3]=='md5' or sys.argv[3]=='sha224'
  137. or sys.argv[3]=='sha256' or sys.argv[3]=='sha512'):
  138. hashAlgorithm = sys.argv[3]
  139. else:
  140. use()
  141. exit(0)
  142. ############## Programa principal
  143. files = getList(directory)
  144. digests = digests(files, hashAlgorithm)
  145. duplicados = 0
  146. completados = []
  147. erroneos = []
  148. for digest, lista in digests.items():
  149. # verbose
  150. #print (digest, lista)
  151. if len(lista) > 1:
  152. # significa que hay archivos duplicados
  153. listaDuplicados = []
  154. duplicados += 1
  155. fuente = lista[0]
  156. c = 0
  157. for file in lista:
  158. listaDuplicados.append(file)
  159. if eliminar_duplicados and c > 0:
  160. if eliminarArchivo(file):
  161. completados.append(file)
  162. else:
  163. erroneos.append(file)
  164. if crear_enlaces_simbolicos and c > 0:
  165. if eliminarYCrearEnlaceSimbolico(fuente, file):
  166. completados.append(file)
  167. else:
  168. erroneos.append(file)
  169. c += 1
  170. # mostrando
  171. for file in listaDuplicados:
  172. print (file)
  173. print (len(listaDuplicados))
  174. print ()
  175. print ('')
  176. print ('# completados satisfactoriamente #')
  177. for file in completados:
  178. print (file)
  179. print ('')
  180. print ('# errores generados #')
  181. for file in erroneos:
  182. print (file)
  183. print ('----')
  184. print (' - Total Duplicados:', str(duplicados))
  185. print (' - Completados:', str(len(completados)))
  186. print (' - Erroneos:', str(len(erroneos)))
  187. exit(0)