11

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?

4 Answers 4

9

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

diff --recursive --no-dereference tree1root tree2root

The --no-dereference will prevent diff from following any symlinks. It will still compare the targets and let you know if they differ.

1
  • 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
    Commented Aug 23, 2014 at 22:31
4

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.

2

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
6
  • @StephaneChazelas That's my error, I meant to use -m, not -f. Thanks!
    – Chris Down
    Commented Nov 11, 2013 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. Commented Nov 11, 2013 at 11:26
  • I would not use -m either. Commented Nov 11, 2013 at 11:28
  • @StephaneChazelas For what reason? Without it, relative symlinks that really point to the same file will fail this comparison.
    – Chris Down
    Commented Nov 11, 2013 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 ]]). Commented Nov 11, 2013 at 11:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .