Monday, February 7, 2011

How to compare 2 symbolic links in unix (Linux)?

What would be an elegant way of comparing 2 symbolic links in Unix?

Assume I have entered the following commands:

ln -s /usr/share/g s1
ln -s /usr/share/g s2
ln -s ../share/g /usr/lib/s3
ln -s /usr/share/h s4

then I would like to have a command that says that s1 and s2 are equal (whether /usr/share/g actually exists is not relevant), that s1 and s4 are not equal, s2 and s4 are not equal. (For my purpose it suffices that s3 is reported to differ from s1 and s2; however if comparison after path normalization could be done, that may be useful to.)

  • You could do a stat -L on the file and compare device and inode number. Quick example:

    #!/bin/bash
    DI_A=$(stat -c "%d.%i" -L "$1")
    DI_B=$(stat -c "%d.%i" -L "$2")
    
    if [ "$DI_A" == "$DI_B" ]; then
       echo "same file"
    else
       echo "other file"
    fi
    
    Dong Hoon : It doesn't seem to work on dangling links.
    From bdijkstra
  • Either use stat -L or parse the output of ls -l (with sed or cut or similar):

    fileone=`ls -l s1 | sed 's/.*-> //'`
    filetwo=`ls -l s2 | sed 's/.*-> //'`
    if [ "$fileone" = "$filetwo" ]
    then
        echo "They're the same!"
    fi
    

    To normalise the path, use realpath:

    fileone=`ls -1 s1 | sed 's/.*-> //'`
    fileone=`realpath $fileone`
    # etc...
    
    Dong Hoon : breaks on filenames containing "->"... thanks for the realpath tip though.
    Al : True... can't say I've ever had any files with -> in them. Readlink is definitely the way to go.
    From Al
  • 2 ways i can think of, you can use ls -l on both files. you can see after the "->" of the output the actual path.eg

    # ls -ltr msdfasdffff
    lrwxrwxrwx 1 root root 10 Jun 30 19:05 msdfasdffff -> blah.sh
    

    you can get the file name using

    # ls -l msdfasdffff  | awk -F"-> " '{print $NF}'
    blah.sh
    

    then you can compare with another link.

    the other way is use find

    # var1=$(find . -name "s1" -printf "%l")
    # var2=$(find . -name "s2" -printf "%l")
    

    then you can compare the 2 variables using if/else

    Dong Hoon : thx, but the awk-method breaks on filenames containing "->" as a substring; the find-method works, but a few conditions are lacking to restrict search to one file; i'd prefer the readlink command then.
    From ghostdog74
  • For GNU systems (and possibly others, but I can't say), there's readlink(1):

    $ touch a
    $ ln -s a b
    $ readlink b
    a
    

    You can use that in comparisons:

    $ test $(readlink -f a) = $(readlink -f b)
    $ echo $?
    0
    
    Al : +1 Ooh, I didn't know about that one!
    Dong Hoon : Thanks! This is what I was looking for.
    jleedev : @Torsten The stat (readlink) utility appeared in NetBSD 1.6.

0 comments:

Post a Comment