Encuentra los archivos existentes en un directorio pero no en el otro [cerrado]

295

Estoy tratando de encontrar los archivos existentes en un directorio pero no en el otro, intenté usar este comando:

diff -q dir1 dir2

El problema con el comando anterior en el que encuentra los archivos dir1pero no dentro dir2, así como los archivos dir2pero no dentro dir1,

Estoy tratando de encontrar los archivos dir1pero no dir2solo en ellos .

Aquí hay una pequeña muestra de cómo se ven mis datos

dir1    dir2    dir3
1.txt   1.txt   1.txt
2.txt   3.txt   3.txt
5.txt   4.txt   5.txt
6.txt   7.txt   8.txt

Otra pregunta en mi mente es ¿cómo puedo encontrar los archivos dir1pero no dentro dir2o dir3en un solo comando?

Error 404
fuente

Respuestas:

390
diff -r dir1 dir2 | grep dir1 | awk '{print $4}' > difference1.txt

Explicación:

  • diff -r dir1 dir2 muestra qué archivos están solo en dir1 y aquellos solo en dir2 y también los cambios de los archivos presentes en ambos directorios, si los hay.

  • diff -r dir1 dir2 | grep dir1 muestra qué archivos están solo en dir1

  • awk para imprimir solo el nombre del archivo.

asclepix
fuente
55
grepPor algo me gustaría ^dir1asegurarme de no dir1aparecer más tarde en el camino.
Alfe
@Alfe Se puede mejorar. Yo uso $4como ejemplo. De hecho, en mi Ubuntu real, las diffrespuestas en italiano. $4está bien para respuestas en italiano e inglés, pero no estoy seguro para todos los demás idiomas ...
asclepix
139

Esto debería hacer el trabajo:

diff -rq dir1 dir2

Opciones explicadas (a través de la página man diff (1) ):

  • -r - Compare recursivamente cualquier subdirectorio encontrado.
  • -q - Salida solo si los archivos difieren.
tokhi
fuente
8
¡Agradable! Pero creo que debería extenderse así:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch
2
Esta es una comparación por contenido, pero puede llevar mucho tiempo en unidades lentas.
Smeterlink
55
Solo una nota sobre la -qopción: las páginas man solo dicen "Mostrar solo si los archivos son diferentes", no cómo comprueba si son diferentes. Leí el código fuente y descubrí que solo verifica los tamaños de archivo para determinar las diferencias, no el contenido real.
ryancdotnet
En cuanto a la -qopción, no puedo reproducir que solo compruebe el tamaño del archivo. Usando GNU Diffutils 3.7 comparando dos archivos con el mismo tamaño de archivo pero diferente contenido con diff -q file1 file2salidas Files file1 and file2 differ.
Stefan Schmidt
50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

Este comando le dará archivos que están en dir1 y no en dir2.

Sobre el <( )signo, puede buscarlo en Google como 'sustitución de proceso'.

plhn
fuente
estaría bien trabajar también con subdirectorios, creo que (ls -R dir1|sort)podría hacer el truco
ulkas
1
Esto funcionaría en el modo de recuperación de OS X.
Anthony Vanover
@ulkas, la salida podría ser incorrecta si usa (ls -R dir|sort) .
Andriy Makukha
3
vimdiff proporciona una comparación visual mucho más agradable con resaltado de color: vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed
32

Una buena manera de hacer esta comparación es usar findcon md5sum, luego a diff.

Ejemplo:

Use findpara enumerar todos los archivos en el directorio, luego calcule el hash md5 para cada archivo y canalícelo a un archivo:

find /dir1/ -type f -exec md5sum {} \; > dir1.txt

Realice el mismo procedimiento para el otro directorio:

find /dir2/ -type f -exec md5sum {} \; > dir2.txt

Luego compare el resultado dos archivos con "diff":

diff dir1.txt dir2.txt

Esta estrategia es muy útil cuando los dos directorios a comparar no están en la misma máquina y debe asegurarse de que los archivos sean iguales en ambos directorios.

Otra buena forma de hacer el trabajo es usar git

git diff --no-index dir1/ dir2/

¡Atentamente!

Adail Junior
fuente
1
No fui a git podría hacer una diferencia en directorios arbitrarios que no están dentro de un repositorio de git ... ¡¡¡increíble !!! Esta respuesta me resolvió un gran problema, gracias
ViktorNova
17

Meld ( http://meldmerge.org/ ) hace un gran trabajo al comparar directorios y los archivos que contiene.

Fusionar directorios comparativos

Catalin Hritcu
fuente
Excepto que Meld hace un mal trabajo cuando se trata de terminaciones de línea ...
0xC0000022L
1
Nunca tuve un problema con los finales de línea. ¿Puedes detallar?
Catalin Hritcu
Sí, no indica los finales de línea. Esto ha llevado (repetidamente) a los desarrolladores a utilizar esta herramienta para confirmar cambios que "arreglaron" las terminaciones de línea al hacer un CRLF en CRLFLF, por ejemplo.
0xC0000022L
3
También insiste en leer el contenido del archivo y, por lo tanto, es casi inútil con los directorios >> 1GB.
Tomislav Nakic-Alfirevic
13

El complemento DirDiff de vim es otra herramienta muy útil para comparar directorios.

vim -c "DirDiff dir1 dir2"

No solo enumera qué archivos son diferentes entre los directorios, sino que también le permite inspeccionar / modificar con vimdiff los archivos que son diferentes.

drrossum
fuente
11

Insatisfecho con todas las respuestas, dado que la mayoría de ellas funcionan muy lentamente y producen resultados innecesariamente largos para directorios grandes, escribí mi propio script de Python para comparar dos carpetas.

A diferencia de muchas otras soluciones, no compara el contenido de los archivos. Además, no va dentro de subdirectorios que faltan en otro directorio. Por lo tanto, el resultado es bastante conciso y el script funciona rápido.

#!/usr/bin/env python3

import os, sys

def compare_dirs(d1: "old directory name", d2: "new directory name"):
    def print_local(a, msg):
        print('DIR ' if a[2] else 'FILE', a[1], msg)
    # ensure validity
    for d in [d1,d2]:
        if not os.path.isdir(d):
            raise ValueError("not a directory: " + d)
    # get relative path
    l1 = [(x,os.path.join(d1,x)) for x in os.listdir(d1)]
    l2 = [(x,os.path.join(d2,x)) for x in os.listdir(d2)]
    # determine type: directory or file?
    l1 = sorted([(x,y,os.path.isdir(y)) for x,y in l1])
    l2 = sorted([(x,y,os.path.isdir(y)) for x,y in l2])
    i1 = i2 = 0
    common_dirs = []
    while i1<len(l1) and i2<len(l2):
        if l1[i1][0] == l2[i2][0]:      # same name
            if l1[i1][2] == l2[i2][2]:  # same type
                if l1[i1][2]:           # remember this folder for recursion
                    common_dirs.append((l1[i1][1], l2[i2][1]))
            else:
                print_local(l1[i1],'type changed')
            i1 += 1
            i2 += 1
        elif l1[i1][0]<l2[i2][0]:
            print_local(l1[i1],'removed')
            i1 += 1
        elif l1[i1][0]>l2[i2][0]:
            print_local(l2[i2],'added')
            i2 += 1
    while i1<len(l1):
        print_local(l1[i1],'removed')
        i1 += 1
    while i2<len(l2):
        print_local(l2[i2],'added')
        i2 += 1
    # compare subfolders recursively
    for sd1,sd2 in common_dirs:
        compare_dirs(sd1, sd2)

if __name__=="__main__":
    compare_dirs(sys.argv[1], sys.argv[2])

Uso de la muestra:

user@laptop:~$ python3 compare_dirs.py dir1/ dir2/
DIR  dir1/out/flavor-domino removed
DIR  dir2/out/flavor-maxim2 added
DIR  dir1/target/vendor/flavor-domino removed
DIR  dir2/target/vendor/flavor-maxim2 added
FILE dir1/tmp/.kconfig-flavor_domino removed
FILE dir2/tmp/.kconfig-flavor_maxim2 added
DIR  dir2/tools/tools/LiveSuit_For_Linux64 added

O si desea ver solo los archivos del primer directorio:

user@laptop:~$ python3 compare_dirs.py dir2/ dir1/ | grep dir1
DIR  dir1/out/flavor-domino added
DIR  dir1/target/vendor/flavor-domino added
FILE dir1/tmp/.kconfig-flavor_domino added

PD: si necesita comparar tamaños de archivo y hashes de archivos para posibles cambios, publiqué un script actualizado aquí: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779

Andriy Makukha
fuente
Script suficientemente simple que hace exactamente lo que quería: verificar una copia masiva: +1 de mí. (aunque es necesario convertir a python2) Sugerencia: el uso de conjuntos podría simplificar la parte diff.
Jason Morgan
6

Otro enfoque (quizás más rápido para directorios grandes):

$ find dir1 | sed 's,^[^/]*/,,' | sort > dir1.txt && find dir2 | sed 's,^[^/]*/,,' | sort > dir2.txt
$ diff dir1.txt dir2.txt

El sedcomando elimina el primer componente del directorio gracias a la publicación de Erik )

jaltek
fuente
1
Creo que este método es más simple (sigue usando, por lo findtanto, un comentario y no una respuesta por separado): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null esto imprimirá los archivos presentes en dir2 pero no en dir1.
Alexander Amelkin
5

Esto es un poco tarde pero puede ayudar a alguien. No estoy seguro de si diff o rsync escupen solo nombres de archivo en un formato simple como este. Gracias a plhn por dar esa buena solución que amplié a continuación.

Si desea solo los nombres de archivo, por lo que es fácil copiar los archivos que necesita en un formato limpio, puede usar el comando find.

comm -23 <(find dir1 | sed 's/dir1/\//'| sort) <(find dir2 | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Esto supone que tanto dir1 como dir2 están en la misma carpeta principal. sed simplemente elimina la carpeta principal para que pueda comparar manzanas con manzanas. El último sed simplemente devuelve el nombre dir1.

Si solo quieres archivos:

comm -23 <(find dir1 -type f | sed 's/dir1/\//'| sort) <(find dir2 -type f | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Del mismo modo para directorios:

comm -23 <(find dir1 -type d | sed 's/dir1/\//'| sort) <(find dir2 -type d | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'
James
fuente
1
Tenga en cuenta que se podría hacer cdantes de que el findlugar de tener que utilizar sed, por ejemplo: comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort). (Los exits están aquí para evitar el finduso del directorio actual deberían cdfallar)
Phk
También tenga en cuenta que su solución puede fallar cuando hay archivos con ciertos caracteres especiales, si tiene una versión muy reciente commcon soporte -z(vino con git.savannah.gnu.org/cgit/coreutils.git/commit/… ) puede hacerlo comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z). (Mientras tanto, también descubrí que los exits podrían ser reemplazados.)
phk
5

La respuesta aceptada también mostrará una lista de los archivos que existen en ambos directorios, pero que tienen contenido diferente. Para enumerar SOLO los archivos que existen en dir1 puede usar:

diff -r dir1 dir2 | grep 'Only in' | grep dir1 | awk '{print $4}' > difference1.txt

Explicación:

  • diff -r dir1 dir2: comparar
  • grep 'Solo en': obtener líneas que contienen 'Solo en'
  • grep dir1: obtiene líneas que contienen dir
Aris
fuente
5

Esta respuesta optimiza una de las sugerencias de @ Adail-Junior al agregar la -Dopción, que es útil cuando ninguno de los directorios que se comparan son repositorios git:

git diff -D --no-index dir1/ dir2/

Si usa, -Dentonces no verá comparaciones con /dev/null: text Binary files a/whatever and /dev/null differ

Mike Slinn
fuente
Fue muy útil al comparar dos directorios, ves instantáneamente las diferencias entre los archivos. Por supuesto, funciona mejor en archivos con contenido de texto.
Erich Kuester
1

Una forma simplificada de comparar 2 directorios con el comando DIFF

diff filename.1 filename.2> filename.dat >> Enter

abrir filename.dat después de que se complete la ejecución

y verá: Solo en nombre de archivo.1: nombre de archivo.2 Solo en: nombre_de_directorio: nombre_de_archivo1 Solo en: nombre_de_directorio: nombre_de_archivo2

nerakk
fuente
¿Por qué tiene que generar un archivo .dat?
Vishnu NK
1

Este es el script bash para imprimir comandos para sincronizar dos directorios

dir1=/tmp/path_to_dir1
dir2=/tmp/path_to_dir2
diff -rq $dir1 $dir2 | sed -e "s|Only in $dir2\(.*\): \(.*\)|cp -r $dir2\1/\2 $dir1\1|" |  sed -e "s|Only in $dir1\(.*\): \(.*\)|cp -r $dir1\1/\2 $dir2\1|" 
Ebru Yener
fuente
0

GNU greppuede invertir la búsqueda con la opción -v. Esto hace grepque el informe de las líneas, que no coinciden. Con esto, puede eliminar los archivos dir2de la lista de archivos dir1.

grep -v -F -x -f <(find dir2 -type f -printf '%P\n') <(find dir1 -type f -printf '%P\n')

Las opciones le -F -xindican grepque realice una búsqueda de cadena en toda la línea.

ceving
fuente