Question Comparer le contenu de deux répertoires


J'ai deux répertoires qui doivent contenir les mêmes fichiers et avoir la même structure de répertoires.

Je pense que quelque chose manque dans l'un de ces répertoires.

En utilisant le shell bash, existe-t-il un moyen de comparer mes répertoires et de voir si l'un d'entre eux manque des fichiers présents dans l'autre?


66
2018-02-16 16:54


origine


Quelle est la sortie de bash --version? - jobin
Similaire mais plus spécifique: stackoverflow.com/questions/16787916/… - Ciro Santilli 新疆改造中心 六四事件 法轮功


Réponses:


Une bonne façon de faire cette comparaison est d'utiliser find avec md5sum, puis un diff.

Exemple

Utilisez find pour lister tous les fichiers du répertoire puis calculez le hash md5 pour chaque fichier et dirigez-le vers un fichier trié par nom de fichier:

find /dir1/ -type f -exec md5sum {} + | sort -k 2 > dir1.txt

Procédez de la même manière dans un autre répertoire:

find /dir2/ -type f -exec md5sum {} + | sort -k 2 > dir2.txt

Puis comparez le résultat deux fichiers avec diff:

diff -u dir1.txt dir2.txt

Ou en tant que commande unique utilisant la substitution de processus:

diff <(find /dir1/ -type f -exec md5sum {} + | sort -k 2) <(find /dir2/ -type f -exec md5sum {} + | sort -k 2)

Cette stratégie est très utile lorsque les deux répertoires à comparer ne sont pas dans la même machine et que vous devez vous assurer que les fichiers sont égaux dans les deux répertoires.

Un autre bon moyen de faire le travail consiste à utiliser diff commande (peut causer des problèmes lorsque les fichiers ont des autorisations différentes -> chaque fichier est répertorié dans la sortie alors):

git diff --no-index dir1/ dir2/

43
2018-01-09 20:05



Cela ne fonctionne pas sans une étape de tri supplémentaire, car l'ordre dans lequel find listera les fichiers différant généralement entre les deux répertoires. - Faheem Mitha
On peut utiliser la méthode décrite dans askubuntu.com/a/662383/15729 pour trier les fichiers. - Faheem Mitha
J'obtiens l'erreur `` find: md5sum: Pas de tel fichier ou répertoire - Houman
@Houman Je ne sais pas ce que vous utilisez sous Linux Distro, mais vous devrez peut-être installer un paquet qui fournira de md5sum. Dans Fedora 26, vous pouvez l'installer avec: #dnf install coreutils - Adail Junior
@AdailJunior Je suis sur Mac Sierra. Merci - Houman


Vous pouvez utiliser le diff commande comme vous l'utiliseriez pour les fichiers:

diff <directory1> <directory2>

Si vous voulez voir des sous-dossiers et des fichiers, vous pouvez utiliser le -r option:

diff -r <directory1> <directory2>

58
2018-02-16 16:59



Je ne savais pas diff fonctionne également pour les répertoires (cela a été confirmé par man diff), mais cela ne vérifie pas de manière récursive les modifications dans les sous-répertoires à l'intérieur des sous-répertoires. - jobin
@Jobin C'est étrange ... Pour moi, ça marche. - Alex R.
J'ai quelque chose comme ça: a/b/c/d/a, x/b/c/d/b. Voir quoi diff a x vous donne. - jobin
Vous devez utiliser le -r option. Cette (diff -r a x) Donne moi: Only in a/b/c/d: a. only in x/b/c/d: b. - Alex R.
diff me montre la différence entre les fichiers INTO mais pas si un répertoire contient un fichier que l'autre ne contient pas !!! Je n'ai pas besoin de connaître les différences dans un fichier mais aussi si un fichier existe dans un répertoire et non dans un autre - AndreaNobili


Grâce à vous n'utilisez pas bash, vous pouvez le faire en utilisant diff avec --brief et --recursive:

$ diff -rq dir1 dir2 
Only in dir2: file2
Only in dir1: file1

le man diff comprend les deux options:

-q, --brief
                signaler uniquement lorsque les fichiers diffèrent

-r, --recursive
                comparer récursivement tous les sous-répertoires trouvés


14
2018-02-16 21:19





Voici une alternative, pour comparer uniquement les noms de fichiers, et non leur contenu:

diff <(cd folder1 && find . | sort) <(cd folder2 && find . | sort)

C'est un moyen facile de lister les fichiers manquants, mais bien sûr ne détectera pas des fichiers avec le même nom mais des contenus différents!

(Personnellement, j'utilise mon propre diffdirs script, mais cela fait partie d'un plus grande bibliothèque.)


13
2018-02-16 17:35



Vous feriez mieux d'utiliser la substitution de processus, pas les fichiers temporaires ... - mniip
Bonne suggestion, merci - joeytwiddle
Notez que cela ne supporte pas les noms de fichiers avec certains caractères spéciaux, dans ce cas, vous pouvez utiliser des délimiteurs zéro qui AFAIK diff ne supporte pas pour le moment. Mais il y a comm qui le soutient depuis git.savannah.gnu.org/cgit/coreutils.git/commit/… donc une fois qu'il s'agit d'un coreutils près de chez vous, vous pouvez faire comm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z) (dont vous pourriez avoir à convertir davantage le format dont vous avez besoin en utilisant le --output-delimiterparamètre et outils supplémentaires). - phk


Si vous voulez rendre chaque fichier extensible et pliable, vous pouvez diriger la sortie de diff -r dans Vim.

Donnons d'abord à Vim une règle de pliage:

mkdir -p ~/.vim/ftplugin
echo "set foldexpr=getline(v:lnum)=~'^diff.*'?'>1':1 foldmethod=expr fdc=2" >> ~/.vim/ftplugin/diff.vim

Maintenant juste:

diff -r dir1 dir2 | vim -

Vous pouvez frapper zo et zc pour ouvrir et fermer les plis. Pour sortir de Vim, frappez :q<Enter>


3
2018-03-06 04:25





Inspiré par la réponse de Sergiy, j'ai écrit mon propre script Python pour comparer deux répertoires.

Contrairement à beaucoup d'autres solutions, il ne compare pas le contenu des fichiers. En outre, il ne va pas dans les sous-répertoires qui manquent dans l'un des répertoires. La sortie est donc assez concise et le script fonctionne rapidement avec les grands répertoires.

#!/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])

Si vous l'enregistrez dans un fichier nommé compare_dirs.py, vous pouvez l'exécuter avec Python3.x:

python3 compare_dirs.py dir1 dir2

Sortie de l'échantillon:

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

P.S. Si vous avez besoin de comparer les tailles de fichiers et les hachages de fichiers pour les modifications potentielles, j'ai publié un script mis à jour ici: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


3
2018-01-16 10:01



Merci, j'ai ajouté un troisième paramètre facultatif regexp pour ignorer / ignorer gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684 faire exactement ce dont j'avais besoin comme: cmpdirs dir1 dir2 '/\.git/' - Mike


Tâche assez facile à réaliser en python:

python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' DIR1 DIR2

Remplacer les valeurs réelles pour DIR1 et DIR2.

Voici un exemple:

$ python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' Desktop/ Desktop
SAME
$ python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' Desktop/ Pictures/
DIFF

Pour plus de lisibilité, voici un script à la place de l’unidirectionnel:

#!/usr/bin/env python
import os, sys

d1 = os.listdir(sys.argv[1])
d2 = os.listdir(sys.argv[2])
d1.sort()
d2.sort()

if d1 == d2:
    print("SAME")
else:
    print("DIFF")

2
2017-11-14 06:12



Notez que le os.listdir ne donne aucun ordre spécifique. Les listes peuvent donc avoir les mêmes choses dans un ordre différent et la comparaison échoue. - muru
@muru bon point, je vais inclure le tri à cela - Sergiy Kolodyazhnyy


Peut-être une option consiste à exécuter rsync deux fois

rsync -r -n -t -v --progress -c -s /dir1/ /dir2/

Avec la ligne précédente, vous obtiendrez des fichiers qui sont dans dir1 et qui sont différents (ou manquants) dans dir2. Aussi des dossiers avec une date différente.

rsync -r -n -t -v --progress -c -s /dir2/ /dir1/

La même chose pour dir2

#from the rsync --help :
-r, --recursive             recurse into directories
-n, --dry-run               perform a trial run with no changes made
-t, --times                 preserve modification times
-v, --verbose               increase verbosity
    --progress              show progress during transfer
-c, --checksum              skip based on checksum, not mod-time & size
-s, --protect-args          no space-splitting; only wildcard special-chars

Vous pouvez supprimer l'option -n pour subir les modifications. Cela copie la liste des fichiers dans le deuxième dossier.

Si vous faites cela, une bonne option est peut-être d'utiliser -u pour éviter d'écraser les fichiers plus récents.

-u, --update                skip files that are newer on the receiver

2
2017-12-16 23:26





Je vais ajouter à cette liste une alternative NodeJs que j'ai écrite il y a quelque temps.

dir-compare

npm install dir-compare -g
dircompare dir1 dir2

0
2018-02-20 20:51