Comparer récursivement deux répertoires avec diff -r sans sortie sur des liens rompus

38

J'utilise diff -r a bpour comparer récursivement les répertoires a et b . Il arrive souvent que qu'il ya des liens cassés (les mêmes liens cassés à la fois un et b répertoires et pointant vers les mêmes cibles, non-existants).

diff émet ensuite des messages d'erreur pour ces cas et les sorties avec un code de sortie non nul, mais j'aimerais qu'il reste silencieux et quitte avec 0 car les répertoires sont les mêmes dans mon livre.

Comment puis je faire ça?

Marcus Junius Brutus
la source
Voulez-vous toujours que les liens symboliques soient comparés (et identifiés comme équivalents mais brisés), ou est-il acceptable d'ignorer tous les liens symboliques lors de cette opération?
ire_and_curses
comparés et identifiés comme équivalents, peu m'importe qu'ils soient cassés. J'essaye juste de vérifier que mon rsync a fonctionné.
Marcus Junius Brutus le

Réponses:

24

Pour la version 3.3 ou ultérieure de diff, vous devriez utiliser l' --no-dereferenceoption, comme décrit dans la réponse de Pete Harlan .

Malheureusement, les anciennes versions de diff ne permettent pas d'ignorer les liens symboliques :

Certains fichiers ne sont ni des répertoires ni des fichiers normaux: ce sont des fichiers inhabituels tels que des liens symboliques, des fichiers spéciaux de périphériques, des canaux nommés et des sockets. Actuellement, diffles liens symboliques sont traités comme des fichiers normaux. il traite les autres fichiers spéciaux comme des fichiers normaux s'ils sont spécifiés au niveau supérieur, mais signale simplement leur présence lors de la comparaison de répertoires. Cela signifie que patchvous ne pouvez pas représenter les modifications apportées à ces fichiers. Par exemple, si vous modifiez le fichier vers lequel pointe un lien symbolique, diffaffiche la différence entre les deux fichiers au lieu de la modification du lien symbolique.

diffdevrait éventuellement signaler les modifications aux fichiers spéciaux spécialement, et patchdevrait être étendu pour comprendre ces extensions.

Si tout ce que vous voulez, c'est vérifier un rsync (et probablement réparer ce qui manque), vous pouvez alors exécuter la commande rsync une seconde fois. Si vous ne voulez pas faire cela, alors sommer le répertoire peut être suffisant.

Si vous voulez vraiment faire cela avec diff, alors vous pouvez utiliser findpour ignorer les liens symboliques et exécuter diff sur chaque fichier individuellement. Passez vos annuaires un et b en arguments:

#!/bin/bash
# Skip files in $1 which are symlinks
for f in `find $1/* ! -type l`
do
    # Suppress details of differences
    diff -rq $f $2/${f##*/}
done

ou comme one-liner:

for f in `find a/* ! -type l`;do diff -rq $f b/${f##*/};done

Ceci identifiera les fichiers dont le contenu diffère ou ceux qui se trouvent dans un répertoire. mais pas dans b .

Notez que:

  • comme nous omettons complètement les liens symboliques, cela ne se produira pas si les noms des liens symboliques ne sont pas présents dans b . Si vous en aviez besoin, vous auriez besoin d'une deuxième passe de recherche pour identifier tous les liens symboliques, puis vérifier explicitement leur existence dans b .
  • Les fichiers supplémentaires dans b ne seront pas identifiés, car la liste est construite à partir du contenu de a . Ce n'est probablement pas un problème pour votre rsyncscénario.
ire_et_curses
la source
Le script proposé ne fonctionne pas récursivement pour les répertoires présents dans le répertoire 'a' (les chemins créés pour 'b' à l'aide de b / $ {f ## *} ne sont pas corrects).
Marcus Junius Brutus
@MarcusJuniusBrutus - Oui, vous avez raison. Je pense que la solution est de supprimer un #, par exemple, for f in trouver un / *! -type l ;do echo $f b/${f#*/};done. Cependant, je n'ai pas le temps de tester cela maintenant. Faites-moi savoir si cela fonctionne.
ire_and_curses
Il est mieux mais il salit encore les chemins de fichiers dans de nombreux cas. Le script (avec un # supprimé) semble devoir être appelé à partir d'un répertoire directement sur "a" pour fonctionner.
Marcus Junius Brutus
Cette réponse devient obsolète avec l’utilisation de GNU diff 3.3 (voir messages ci-dessous)
Bernd Gloss
Le script ci-dessus présente plusieurs problèmes, dus au fait que tous les noms de fichiers ont été trouvés et qu’ils ont été alimentés par une ligne de commande étendue. (1) Il ne fonctionnera qu'avec de petites collections de fichiers. (2) Aucun nom de fichier avec un caractère spécial (même un espace) ne sera traité. (3) Toujours utiliser $(xxx)au lieu de backticks. La symétrie des backticks les rend moins lisibles et empêche leur imbrication. En ce qui concerne 1 et 2, voir stackoverflow.com/questions/11366184/…
Stéphane Gourichon
19

Depuis la version 3.3, GNU diffne prend pas en charge le déréférencement des liens symboliques, mais compare ensuite les chemins qu’ils pointent.

Installez GNU diffutils> = 3.3 et utilisez l’ --no-dereferenceoption; il n'y a pas d'option courte pour cela.

Le diagnostic sera muet si égal ou:

Liens symboliques /tmp/noderef/a/symlinket /tmp/noderef/b/symlinkdifférer

Philippe De Muyter
la source
Maintenant si seulement il montrerait les changements de contenu, comme si le lien symbolique était un fichier normal ...: - /
lindes
6

Vous pouvez utiliser une version plus récente de diff

La diffGNU diffutils3.3 inclut une --no-dereferenceoption qui vous permet de comparer les liens symboliques eux - mêmes plutôt que leurs objectifs. Il signale s’ils diffèrent, reste silencieux s’ils sont d’accord et ne se soucie pas de savoir s’ils sont brisés.

Je ne sais pas quand l'option a été ajoutée. ce n'est pas présent dans 2.8.1.

Pete Harlan
la source
Je peux confirmer qu’il n’existe pas en diff (GNU diffutils) 3.2 non plus
Elder Geek