Search code examples
linuxshellsymlinkhardlinkln

Why does the Linux shell command test -ef not work on soft links as its manual describes?


I create a hard link to student.sh and a soft link to student.sh. The hard links have different inodes than soft links. But when comparing the soft with student.sh by using the test -ef command, it does not work as expected. I had checked the manuals of the ln and test commands but still confused with them.

$ man test

# FILE1 -ef FILE2
#   FILE1 and FILE2 have the same device and inode numbers
$ ln /home/pi/Desktop/links/student.sh hard
$ ln -s /home/pi/Desktop/links/student.sh soft

$ ls -ial
278224 drwxr-xr-x  2 pi pi 4096  6月  5 23:31 .
262125 drwxr-xr-x 12 pi pi 4096  6月  5 23:17 ..
278227 -rwxr-xr-x  2 pi pi   43  6月  5 23:20 hard
278225 lrwxrwxrwx  1 pi pi   33  6月  5 23:31 soft -> /home/pi/Desktop/links/student.sh
278227 -rwxr-xr-x  2 pi pi   43  6月  5 23:20 student.sh

test

# ❌ inode is different
$ [ ./student.sh -ef ./soft ] && echo yes || echo no
yes

$ [ ./student.sh -ef ./hard ] && echo yes || echo no
yes

update test case

./test.sh

#!/usr/bin/env bash

if [[ ./student.sh -ef ./soft ]]; then
  echo "yes"
else
  echo "no"
fi

if [[ ./student.sh -ef ./hard ]]; then
  echo "yes"
else
  echo "no"
fi
$ ./test.sh 
yes
yes

wanted

# inode is different, so it should output no ✅
$ [ ./student.sh -ef ./soft ] && echo yes || echo no
no

$ [ ./student.sh -ef ./hard ] && echo yes || echo no
yes

Solution

  • Like Barmar mentioned in a comment, and as documented in the bash manual section on conditional expressions:

    Unless otherwise specified, primaries that operate on files follow symbolic links and operate on the target of the link, rather than the link itself.

    You can use stat(1) to get the device number and inode without following symbolic links, and compare those values, though:

    $ is_same_file () { test "$(stat -c "%d %i" "$1")" = "$(stat -c "%d %i" "$2")"; }
    $ if is_same_file student.sh hard; then echo yes; else echo no; fi
    yes
    $ if is_same_file student.sh soft; then echo yes; else echo no; fi
    no
    

    This should work on any POSIXish shell, not just bash, as long as the GNU coreutils stat(1) command is available, which seems a safe bet since you're talking about Linux. Might need to be adjusted if using some other enviroment; the NetBSD stat(1), for example, uses -f instead of -c).