8

In Linux one can use the command diff to check for differences of files. I use it to check for differences across two modules, both copied to /tmp.

However, both modules have symlinks, which might not be pointing to the correct file, as the modules have been copied to the /tmp directory. But this does not matter. What I want to check is if the symlinks are the same.

For example, in one module the symlink reads

/home/test/file1

and in the other the symlink reads:

/etc/rc.d/whatever

diff now throws an error because - in general - it cannot find the file. But I want to let diff say: "Hey, those symlinks are not the same" regardless if the links point to valid files or not.

Question: How is it possible for diff to do that, i.e. not trying to follow links, but to diff the links itself?

6

Try GNU diff version 3.3 or later with the --no-dereference option (and presumably the --recursive option):

diff --recursive --no-dereference tree1root tree2root
  • Could you flesh this out a little? Perhaps give an example of how it could be used? This has the makings of a perfectly good answer but we like our answers to be a bit more explicit and verbose here. – terdon Aug 23 '14 at 22:31
3

Since version 3.3, GNU diff supports not dereferencing symlinks, but then compares the paths they point to.

Install GNU diffutils >= 3.3 and use the '--no-dereference' option; there is no short option for that.

Diagnostic will be silent if the paths are equal, or:

Symbolic links /tmp/noderef/a/symlink and /tmp/noderef/b/symlink differ

if the paths differ.

1

With GNU tools:

diff <(cd dir1 && find . -type l -printf '%p -> %l\n'|sort) \
     <(cd dir2 && find . -type l -printf '%p -> %l\n'|sort)

Strictly speaking, that can't differentiate between a symlink called a that points to b -> c and one called a -> b that points to c, not to mention the problems with filenames containing newline characters, but that makes for a more legible output than the more robust:

diff <(cd dir1 && find . -type l -printf '%p // %l\0'|tr '\n\0' '\0\n'|sort) \
     <(cd dir2 && find . -type l -printf '%p // %l\0'|tr '\n\0' '\0\n'|sort)

There, we use // as the separator (that cannot occur otherwise in the output of find for %p) and convert the newline characters to NUL characters (which cannot occur in the expansion of %p nor %l).

1

If I understand correctly, you want to check that the symlinks point to the same theoretical destination, or that if one is not a symlink, the other file links to the same file. This should do what you want:

#!/bin/bash

if [[ "$(readlink -m -- "$1")" == "$(readlink -m -- "$2")" ]]; then
    echo "files match"
else
    echo "files don't match"
    exit 1
fi
$ > foo 
$ ln -s foo bar
$ ln -s foo baz
$ ./script bar baz
files match
$ ./script bar foo
files match
$ ln -sf qux bar
$ ./script bar foo
files don't match
  • @StephaneChazelas That's my error, I meant to use -m, not -f. Thanks! – Chris Down Nov 11 '13 at 11:19
  • Strictly speaking, that wouldn't work if the only difference is the number of trailing newline characters in the targets of the symlinks. – Stéphane Chazelas Nov 11 '13 at 11:26
  • I would not use -m either. – Stéphane Chazelas Nov 11 '13 at 11:28
  • @StephaneChazelas For what reason? Without it, relative symlinks that really point to the same file will fail this comparison. – Chris Down Nov 11 '13 at 11:29
  • If /home/test/file1 is a symlink to /etc/rc.d/whatever, or they're both a symlink to /, it would report they're the same. I think the OP wants to see the difference in the "path" stored in the symlink, not whether they resolve to the same file (for which you have [[ a -ef b ]]). – Stéphane Chazelas Nov 11 '13 at 11:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.