1

TL;DR: why [ symlink_to_file_a -ef file_a ] return true ?


I need to test if a file b (mostly symlink) is the same than a file a for trying to know if a and b are hard linked together, and hard link them if itsn't the case.
I use conditional expression -ef and bash manual says :

file1 -ef file2
True if file1 and file2 refer to the same device and inode numbers.

I compare just symlink/regular file and inodes of a and b are different but the result is True.

An answer of a similar question says :

If yes, then will the inode number be same for target and links?

No. Usually, the symlink is a file with its own inode, (with file-type, own data blocks, etc.)

I'm not sure of what I understood but I may found some explanations about it in ext4 spec :

The target of a symbolic link will be stored in this field if the target string is less than 60 bytes long. Otherwise, either extents or block maps will be used to allocate data blocks to store the link target.

I tried with target shorter/longer than 60B but there is no difference.

$ cat test.sh
#!/usr/bin/env bash
foo="foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
mkdir /tmp/test
cd /tmp/test
touch a
ln -s /tmp/test/a b
ls -li
if [ a -ef b ]
then
  echo b : same device or inode of a
 else
  echo b : different device or inode of a
fi
mkdir /tmp/test/${foo}
cd /tmp/test/${foo}
touch c
ln -s /tmp/test/${foo}/c d
ls -li
if [ c -ef d ]
then
  echo d : same device or inode of c
else
  echo d : different device or inode of c
fi
$ ./test.sh
156490 -rw-rw-r-- 1 msi msi  0 nov.  25 23:55 a                                                                                                                                                                                                
156491 lrwxrwxrwx 1 msi msi 11 nov.  25 23:55 b -> /tmp/test/a                                                                                                                                                                                 
b : same device or inode of a                                                                                                                                                                                                                  
total 4                                                                                                                                                                                                                                        
156494 -rw-rw-r-- 1 msi msi   0 nov.  25 23:55 c
156495 lrwxrwxrwx 1 msi msi 155 nov.  25 23:55 d -> /tmp/test/foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo/c
d : same device or inode of c

Inodes are different but the test succeed and I didn't see what is wrong.

Community
  • 1
  • 1
F4240
  • 148
  • 1
  • 8
  • 1
    Note that near the top of the section of the manual you link to, it says: _Unless otherwise specified, primaries that operate on files follow symbolic links and operate on the target of the link, rather than the link itself._. That is, except when checking whether something is a symlink (roughly), it uses the `stat()` system call and not the `lstat()` system call. The `ext4` information is simply about the disk storage for the path that the symlink points to; it doesn't affect the functionality (but marginally affects performance since an extra disk block must be read for very long symlinks). – Jonathan Leffler Nov 26 '16 at 22:20
  • I thought that the specific instruction will be exhaustive :/ Thanks for this full explanation. – F4240 Nov 27 '16 at 20:42

1 Answers1

2

Seems like documentation is misleading in some places. From http://www.tldp.org/LDP/abs/html/fto.html:

f1 -ef f2

files f1 and f2 are hard links to the same file

As you already found out yourself, that expression returns true not only for hard links but also for soft links to the same file.

Use stat instead:

if [ "$(stat -c "%d:%i" FILE1)" == "$(stat -c "%d:%i" FILE2)" ]

Source: https://superuser.com/questions/196572/check-if-two-paths-are-pointing-to-the-same-file

Community
  • 1
  • 1
Ruud Helderman
  • 10,563
  • 1
  • 26
  • 45