Script con argumentos en la consola

Siguiendo con el ejemplo del limpiador de duplicados, aprovecho para añadir la posibilidad de elegir el directorio a «limpiar» sin tener que tocar el código. En este caso, se añade la posibilidad de introducir el directorio como argumento del script al ejecutarlo mediante la consola de comandos.

Para poder acceder a los argumentos, es necesario importar el módulo sys que nos permitirá acceder a su función argv que, como en otros lenguajes de programación como C/C++, devuelve una estructura tipo array cuyas posiciones contienen los argumentos introducidos.

import sys

Por ejemplo, si introducimos la siguiente línea de comandos

$> python ejemplo.py arg1 arg2

en este caso, los contenidos de argv[n] serán:

argv[0] = ejemplo.py
argv[1] = arg1
argv[2] = arg2

Por lo tanto, para acceder al primer argumento, usaremos argv[1]. El objetivo es introducir la siguiente línea de comandos

$> python limpiador.py "C:/Users/Fulanito/pruebas/"

y que el script acceda al directorio indicado y proceda a limpiar los duplicados.

Esto es tan sencillo como cambiar la antigua asignación de la variable ruta por el primer argumento introducido por comando (sin contar el propio nombre del script)

import sys

path = "C:/Users/Fulanito/pruebas/"
path = sys.argv[1]

El resto del código, referente a la funcionalidad de encontrar y eliminar duplicados, es el mismo que el del post anterior y puedes verlo aquí.

Limpiando duplicados con Python

Problema

Si descargas muchos archivos pequeños (pdf, doc, xls…), lo más probable es que vayas al sitio origen a descargarlo otra vez antes que buscarlo en la carpeta Descargas. Esto, a la larga, puede resultar en una gran cantidad de archivos duplicados en el mismo directorio.

Esto puede resolverse de dos maneras:

  1. Manualmente: revisando los ficheros con el mismo nombre pero con una numeración entre paréntesis al final del archivo y… Bueno, si no voy a hacerlo así no sé para qué lo explico.
  2. Ingenierilmente: mediante un script que analice un directorio concreto de nuestro ordenador y elimine los ficheros idénticos, conservando el original.

Para hacerlo más legible y compacto, he decidido implementar el script en Python 3.5.

Solución

Los modulos a importar serán los siguientes:

from filecmp import cmp
from os import listdir
from os.path import isfile, join

Esto nos permitirá manejar funcionalidades dependientes del sistema operativo para comparar, abrir y gestionar ficheros. De manera resumida, estas son:

  • filecmp.cmp(f1, f2, shallow=True): devuelve True si los ficheros f1 y f2 son idénticos.
  • os.listdir(‘path’): devuelve una Lista con los nombres de los ficheros contenidos en el directorio indicado por ‘path’.
  • os.path.isfile(‘path’): devuelve True si el fichero indicado por ‘path’ es un fichero real y existente y False en caso contrario.
  • os.path.join(‘path_1’, ‘path_2’): une dos rutas (path). En este caso, une la ruta con el nombre del fichero, ambos pasados por parámetro.

Para empezar se introducen todos los ficheros de una localización específica (ruta) en una Lista (ficheros). Es mejor guardar la ruta del directorio en una variable, ya que se va a usar bastante a lo largo del script.

ruta = "C:/Users/Fulanito/pruebas"  # por ejemplo
ficheros = [f for f in listdir("ruta") if
                 isfile(join("ruta", f))]

Ahora que se tienen los ficheros en una estructura de datos (Lista), se recorre comparando cada fichero con el resto.

for fichero in ficheros:
    for comp in ficheros:
        print(str(file) + " | " + str(comp))

En la línea 3 se imprimen los nombres de los dos ficheros que se compararán en cada iteración (fichero y comp). De esta manera podemos comprobar que está recorriendo correctamente todos los ficheros del directorio.

Para comparar se añade la función cmp() del modulo filecmp que recibe como argumentos los ficheros a comparar y un shallow=True. Al igual que la ruta del directorio, almacenaremos el resultado de dicha comparación en una variable llamada veredicto.

for fichero in ficheros:
    for comp in ficheros:
        if ficheros != comp:
            veredicto = filecmp.cmp(ruta + fichero, ruta + comp, shallow=True)
            print(fichero + " | " + comp + " | " + str(veredicto))

Se ha añadido una estructura de control de flujo if para no comparar al fichero consigo mismo. A la impresión por pantalla del anterior paso le sumamos este veredicto (booleano) para comprobar la ejecución.

Finalmente solo nos queda eliminar los duplicados tanto de la lista que habíamos creado (ficheros) como del directorio (ruta_objetivo). Para ello será necesario el uso de las funciones unlink() del módulo os y remove() de la implementación de listas en Python.

for fichero in ficheros:
    for comp in ficheros:
        if fichero != comp:
            veredicto = filecmp.cmp(ruta + fichero, ruta + comp, shallow=True)
            print(file + " | " + comp + " | " + str(veredicto))
            if veredicto:
                print("ELIMINANDO ARCHIVO: " + comp)
                only_files.remove(comp)
                os.unlink(ruta + comp)

Importante: Es aconsejable ejecutar este tipo de scripts en un entorno controlado, es decir en un directorio de pruebas, antes de usarlo de forma normal. No queremos dar rienda suelta a un script que se autodestruya, no sin antes llevarse por delante el contenido del directorio analizado.


Fuente: elaboración propia.