¿Cómo obtengo la suma MD5 del contenido de un directorio como una sola suma?

¿Cómo obtengo la suma MD5 del contenido de un directorio como una sola suma?

El programa md5sum no proporciona sumas de verificación para directorios. Quiero obtener una única suma de comprobación MD5 para todo el contenido de un directorio, incluidos los archivos de los subdirectorios. Es decir, una suma de comprobación combinada formada por todos los archivos. ¿Hay alguna forma de hacer esto?

Respuesta1

La forma correcta depende exactamente de por qué preguntas:

Opción 1: comparar solo datos

Si solo necesita un hash del contenido del archivo del árbol, esto funcionará:

$ find -s somedir -type f -exec md5sum {} \; | md5sum

Esto primero resume todo el contenido del archivo individualmente, en un orden predecible, luego pasa esa lista de nombres de archivos y hashes MD5 para que se apliquen hash, dando un valor único que solo cambia cuando cambia el contenido de uno de los archivos en el árbol.

Lamentablemente, find -ssólo funciona con BSD find(1), utilizado en macOS, FreeBSD, NetBSD y OpenBSD. Para obtener algo comparable en un sistema con GNU o SUS find(1), necesitas algo un poco más feo:

$ find somedir -type f -exec md5sum {} \; | sort -k 2 | md5sum

Hemos imitado el comportamiento de BSD find -sagregando una llamada a sort. El -k 2bit le indica que omita el hash MD5, por lo que solo ordena los nombres de los archivos, que están desde el campo 2 hasta el final de la línea según sortel cálculo de 's.

Hay una debilidad con esta versión del comando, que es que es probable que se confunda si tiene nombres de archivos con nuevas líneas, porque parecerán varias líneas en la sortllamada. La find -svariante no tiene ese problema, porque el recorrido y la clasificación del árbol ocurren dentro del mismo programa find.

En cualquier caso, la clasificación es necesaria para evitar falsos positivos: los sistemas de archivos Unix/Linux más comunes no mantienen los listados de directorios en un orden estable y predecible. Es posible que no se dé cuenta de esto al usar lsy similares, que clasifican silenciosamente el contenido del directorio por usted. Llamar findsin ordenar su salida de alguna manera hará que el orden de las líneas en la salida coincida con el orden en que las devuelva el sistema de archivos subyacente, lo que hará que este comando proporcione un valor hash modificado si cambia el orden de los archivos que se le dan como entrada. incluso si los datos siguen siendo idénticos.

Quizás te preguntes si el -k 2bit del sortcomando GNU anterior es necesario. Dado que el hash de los datos del archivo es un proxy adecuado para el nombre del archivo siempre que el contenido no haya cambiado, no obtendremos falsos positivos si descartamos esta opción, permitiéndonos usar el mismo comando tanto con GNU como con BSD sort. Sin embargo, tenga en cuenta que existe una pequeña posibilidad (1:2 128 con MD5) de que el orden exacto de los nombres de archivos no coincida con el orden parcial que -k 2puede dar el prescindir de ellos si alguna vez se produce una colisión de hash. Sin embargo, tenga en cuenta que si estas pequeñas posibilidades de una discrepancia son importantes para su aplicación, todo este enfoque probablemente esté fuera de discusión para usted.

Es posible que deba cambiar los md5sumcomandos md5o alguna otra función hash. Si elige otra función hash y necesita la segunda forma del comando para su sistema, es posible que deba ajustar el sortcomando en consecuencia. Otra trampa es que algunos programas de suma de datos no escriben ningún nombre de archivo, un buen ejemplo es el antiguo sumprograma Unix.

Este método es algo ineficiente, llama md5sumN+1 veces, donde N es la cantidad de archivos en el árbol, pero ese es un costo necesario para evitar el hash de metadatos de archivos y directorios.

Opción 2: comparar datosyMetadatos

Si necesitas poder detectar esocualquier cosaen un árbol ha cambiado, no solo el contenido del archivo, solicite tarempaquetar el contenido del directorio y luego envíelo a md5sum:

$ tar -cf - somedir | md5sum

Debido a que tartambién ve los permisos de los archivos, la propiedad, etc., esto también detectará cambios en esas cosas, no solo cambios en el contenido del archivo.

Este método es considerablemente más rápido, ya que sólo realiza una pasada sobre el árbol y ejecuta el programa hash sólo una vez.

Al igual que con el findmétodo basado anterior, tarprocesará los nombres de los archivos en el orden en que los devuelve el sistema de archivos subyacente. Es muy posible que en su aplicación pueda estar seguro de que no provocará que esto suceda. Puedo pensar en al menos tres patrones de uso diferentes en los que es probable que ese sea el caso. (No voy a enumerarlos porque estamos entrando en un territorio de comportamiento no especificado. Cada sistema de archivos puede ser diferente aquí, incluso de una versión del sistema operativo a la siguiente).

Si obtiene falsos positivos, le recomiendo elegir la find | cpioopción enLa respuesta de Gilles..

Respuesta2

La suma de comprobación debe ser una representación determinista e inequívoca de los archivos como una cadena. Determinista significa que si colocas los mismos archivos en las mismas ubicaciones, obtendrás el mismo resultado. Inequívoco significa que dos conjuntos diferentes de archivos tienen representaciones diferentes.

Datos y metadatos

Crear un archivo que contenga los archivos es un buen comienzo. Esta es una representación inequívoca (obviamente, ya que puede recuperar los archivos extrayendo el archivo). Puede incluir metadatos de archivos, como fechas y propiedad. Sin embargo, esto todavía no es del todo cierto: un archivo comprimido es ambiguo, porque su representación depende del orden en que se almacenan los archivos y, en su caso, de la compresión.

Una solución es ordenar los nombres de los archivos antes de archivarlos. Si los nombres de sus archivos no contienen nuevas líneas, puede ejecutarlos find | sortpara enumerarlos y agregarlos al archivo en este orden. Tenga cuidado de decirle al archivador que no recurra a los directorios. A continuación se muestran ejemplos con POSIX pax, GNU tar y cpio:

find | LC_ALL=C sort | pax -w -d | md5sum
find | LC_ALL=C sort | tar -cf - -T - --no-recursion | md5sum
find | LC_ALL=C sort | cpio -o | md5sum

Sólo nombres y contenidos, de forma sencilla

Si solo desea tener en cuenta los datos del archivo y no los metadatos, puede crear un archivo que incluya solo el contenido del archivo, pero no existen herramientas estándar para eso. En lugar de incluir el contenido del archivo, puede incluir el hash de los archivos. Si los nombres de los archivos no contienen nuevas líneas y solo hay archivos y directorios normales (sin enlaces simbólicos ni archivos especiales), esto es bastante fácil, pero debes ocuparte de algunas cosas:

{ export LC_ALL=C;
  find -type f -exec wc -c {} \; | sort; echo;
  find -type f -exec md5sum {} + | sort; echo;
  find . -type d | sort; find . -type d | sort | md5sum;
} | md5sum

Incluimos una lista de directorios además de la lista de sumas de verificación, ya que de lo contrario los directorios vacíos serían invisibles. La lista de archivos está ordenada (en una ubicación específica y reproducible; gracias a Peter.O por recordármelo). echosepara las dos partes (sin esto, podrías crear algunos directorios vacíos cuyo nombre parezca md5sumuna salida que también podría pasar por archivos normales). También incluimos una lista de tamaños de archivos, para evitarataques de extensión de longitud.

Por cierto, MD5 está en desuso. Si está disponible, considere usar SHA-2, o al menos SHA-1.

Nombres y datos, admitiendo nuevas líneas en los nombres.

Aquí hay una variante del código anterior que se basa en herramientas GNU para separar los nombres de los archivos con bytes nulos. Esto permite que los nombres de archivos contengan nuevas líneas. Las utilidades de resumen de GNU citan caracteres especiales en su salida, por lo que no habrá nuevas líneas ambiguas.

{ export LC_ALL=C;
  du -0ab | sort -z; # file lengths, including directories (with length 0)
  echo | tr '\n' '\000'; # separator
  find -type f -exec sha256sum {} + | sort -z; # file hashes
  echo | tr '\n' '\000'; # separator
  echo "End of hashed data."; # End of input marker
} | sha256sum

Un enfoque más sólido

Aquí hay un script de Python mínimamente probado que crea un hash que describe una jerarquía de archivos. Toma directorios y contenidos de archivos en cuentas e ignora enlaces simbólicos y otros archivos, y devuelve un error fatal si algún archivo no se puede leer.

#! /usr/bin/env python
import hashlib, hmac, os, stat, sys
## Return the hash of the contents of the specified file, as a hex string
def file_hash(name):
    f = open(name)
    h = hashlib.sha256()
    while True:
        buf = f.read(16384)
        if len(buf) == 0: break
        h.update(buf)
    f.close()
    return h.hexdigest()
## Traverse the specified path and update the hash with a description of its
## name and contents
def traverse(h, path):
    rs = os.lstat(path)
    quoted_name = repr(path)
    if stat.S_ISDIR(rs.st_mode):
        h.update('dir ' + quoted_name + '\n')
        for entry in sorted(os.listdir(path)):
            traverse(h, os.path.join(path, entry))
    elif stat.S_ISREG(rs.st_mode):
        h.update('reg ' + quoted_name + ' ')
        h.update(str(rs.st_size) + ' ')
        h.update(file_hash(path) + '\n')
    else: pass # silently symlinks and other special files
h = hashlib.sha256()
for root in sys.argv[1:]: traverse(h, root)
h.update('end\n')
print h.hexdigest()

Respuesta3

Si su objetivo es simplemente encontrar diferencias entre dos directorios, considere usar diff.

Prueba esto:

diff -qr dir1 dir2

Respuesta4

Usarchecksumdir:

$ pip install checksumdir
$ checksumdir -a md5 assets/js
981ac0bc890de594a9f2f40e00f13872
$ checksumdir -a sha1 assets/js
88cd20f115e31a1e1ae381f7291d0c8cd3b92fad

Más rápidoymás fácilque las otras soluciones bash.

información relacionada