I'm creating a shell script that would take a filename/path to a file and determine if the file is a symbolic link or a hard link.

The only thing is, I don't know how to see if they are a hard link. I created 2 files, one a hard link and one a symbolic link, to use as a test file. But how would I determine if a file is a hard link or symbolic within a shell script?

Also, how would I find the destination partition of a symbolic link? So let's say I have a file that links to a different partition, how would I find the path to that original file?

  • 18
    What do you mean by hard link? All files are hard links. – terdon Nov 12 '14 at 17:55
  • 2
    @terdon ln /foo/bar/ /foo/bar2 makes a hardlink while ln -s /foo/bar /foo/bar2 makes a symlink, thats what he means? – DisplayName Nov 12 '14 at 18:17
  • 16
    @DisplayName yes, but all files are hard links to their inode. That's how Linux file systems work. In your example, bar2 and bar are both hard links, just pointing to the same inode. – terdon Nov 12 '14 at 18:20
  • 12
    @DisplayName yes, they are hard links to other inodes. There is no contradiction here. A file is a link to an inode. That's the definition of a file. In your case, you have these links in different places but that doesn't change the underlying data structure. My point is that both bar and bar2 are equally important. One is not a link to the other, they are both links but point to the same inode. – terdon Nov 12 '14 at 18:28
  • 3
    @Scott no, I'm saying that regular files are hardlinks and that hardlinks created by ln are no different than regular files. – terdon Nov 13 '14 at 18:14

Jim's answer explains how to test for a symlink: by using test's -L test.

But testing for a "hard link" is, well, strictly speaking not what you want. Hard links work because of how Unix handles files: each file is represented by a single inode. Then a single inode has zero or more names or directory entries or, technically, hard links (what you're calling a "file").

Thankfully, the stat command, where available, can tell you how many names an inode has.

So you're looking for something like this (here assuming the GNU or busybox implementation of stat):

if [ "$(stat -c %h -- "$file")" -gt 1 ]; then
    echo "File has more than one name."

The -c '%h' bit tells stat to just output the number of hardlinks to the inode, i.e., the number of names the file has. -gt 1 then checks if that is more than 1.

Note that symlinks, just like any other files, can also be linked to several directories so you can have several hardlinks to one symlink.

  • ok, just to be clear, i can output the number of hardlinks that the file has using the stat command and if its greater then 1, then it has another file linked somewhere on the partition. – k-Rocker Nov 12 '14 at 18:34
  • 1
    @k-Rocker Yes. Then it has a second name somewhere on the partition. – derobert Nov 12 '14 at 18:38
  • 1
    On OS X or *BSD, it's stat -f %l /path/to/file. You could also use gstat -c %h /path/to/file if you have the GNU coreutils installed without their default names (with Homebrew on OS X). – GDP2 Jul 11 '16 at 22:28

An example:

$ touch f1
$ ln f1 f2
$ ln f1 f3
$ ln -s f1 s1
$ ln -s f2 s2
$ ln -s ./././f3 s3
$ ln -s s3 s4
$ ln s4 s5
$ ls -li
total 0
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f1
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f2
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f3
10802345 lrwxrwxrwx 1 stephane stephane 2 Nov 12 19:56 s1 -> f1
10802346 lrwxrwxrwx 1 stephane stephane 2 Nov 12 19:56 s2 -> f2
10802347 lrwxrwxrwx 1 stephane stephane 8 Nov 12 19:56 s3 -> ./././f3
10802384 lrwxrwxrwx 2 stephane stephane 2 Nov 12 19:56 s4 -> s3
10802384 lrwxrwxrwx 2 stephane stephane 2 Nov 12 19:56 s5 -> s3

The f1, f2 and f3 directory entries are the same file (same inode: 10802124, you'll notice the number of links is 3). They are hard links to the same regular file.

s4 and s5 are also the same file (10802384). They are of type symlink, not regular. They point to a path, here s3. Because s4 and s5 are entries of the same directory, that relative path s3 point to the same file (the one with inod 10802347) for both.

If you do a ls -Ll, that is asking to get file information after resolving symlinks:

$ ls -lLi
total 0
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f1
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f2
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f3
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s1
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s2
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s3
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s4
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s5

You'll find they all resolve to the same file (10802124).

You can check if a file is a symlink with [ -L file ]. Similarly, you can test if a file is a regular file with [ -f file ], but in that case, the check is done after resolving symlinks.

hardlinks are not a type of file, they are just different names for a file (of any type).


Using the -h and -L operators of the test command:

-h file 
true if file is a symbolic link

-L file 
true if file is a symbolic link


According to this SO thread, they have the same behavior, but -L is preferred.

  • ok cool, but what about hard links? i checked the tread, but nothing about hard links. If -L returns false, does that mean its a hard link? or just a regular file? – k-Rocker Nov 12 '14 at 17:44
  • 2
    Hard links share the same inode. Also, soft links show an l at the beginning of its ls -l output... I think you may be able to put those rules together in a script, plus the [[ -L file ]] test to see if the given file is either soft or hard. – jimm-cl Nov 12 '14 at 17:46
  • ok, also, how would i find the destination partition of the symbolic link? – k-Rocker Nov 12 '14 at 18:24
  • Be cautious of using this since file not exists return false but it doesn't means hard link. – Fruit Nov 14 '19 at 18:31

you can use readlink FILE; echo $?. This returns 1 when it's a hardlink and 0 when it's a symlink.

From the man page: " When invoked as readlink, only the target of the symbolic link is printed. If the given argu- ment is not a symbolic link, readlink will print nothing and exit with an error."

  • 2
    Be cautious of using this since file not exists return 1 but it doesn't means hard link. – Fruit Nov 14 '19 at 18:34

There are a lot of quite correct answers here, but I don't think anybody really tackled the original misperception. The original question is basically "when I make a symbolic link, it's easy to identify it afterwards. But I can't figure out how to identify a hard link." And yes, the answers basically boil down to "you can't," and more or less explain why, but nobody seems to have acknowledged that, really, it IS confusing and strange.

If you're reading all this and you've figured out what's going on, then you're good; you don't need to read my little bit. If you're still confused, then keep going.

The really really short answer is that a hard link isn't really a link at all, not in the way a symbolic link is. It's a new entry in the directory structure that points to the same bunch of bytes that the original directory entry did, and once you've created it, it is just as 'real' and legitimate as the first one. Every 'normal' file on your drive has at least one hard link; without that, you wouldn't see it in any directory, and would be unable to refer to it or use it. So if you have a file Fred.txt, and you hard link Wilma.txt and Barney.txt to it, all three names (and directory entries) refer to the same file, and they are all equally valid. There isn't any way for the OS to tell that one of the entries was created when you hit "save" in your text editor, and the others were made with the "ln" command.

The OS does have to keep track of how many different entries are pointing to the same file, though. If you delete Wilma.txt, no surprise that you don't free up any space on your drive. But if you delete Fred.txt (the 'original' file), you still won't free up any space on your drive, because the data on the drive that was known as Fred.txt is still also Barney.txt. Only when you delete all of the directory entries will the OS de-allocate the space that the data itself was occupying.

If Barney.txt had been a symbolic link, then deleting Fred.txt would have de-allocated the space, and Barney.txt would now be a broken link. Also, if you move or rename a file that has a symbolic link pointing at it, you'll break the link. But you can move or rename a hard-linked file all you want without breaking the other directory entries that point to that file/data, because all of them are directory entries that refer to the same block of data on the drive (by using the inode # of that data).

[It's two years later, and that last bit confused me for a minute, so I think I'll clarify. If you type "mv ./Wilma.txt ../elsewhere/Betty.txt" it seems like you're moving the file, but in fact, you are not. What you're really doing is removing a line item from the directory list of your current directory, the one that says "the name 'Wilma.txt' is associated with the data that can be found by using inode #######," and adding a new line item to the directory list of directory ../elsewhere that says "the name 'Betty.txt' is associated with the data that can be found via inode #######". This is why you can 'move' a 2 gigabyte file as quickly as a 2 kilobyte file, as long as you're moving them to another location on the same drive.]

Because the OS has to keep track of how many different directory entries are pointing to the same chunk of data, you can tell if a particular file has been hard linked to, even though you can't tell for certain if the directory entry that you're looking at is the 'original' one or not. One way is the "ls" command, specifically "ls -l" (that's a lower-case L after the dash)

To borrow an earlier example....

 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f1

The first letter's a dash, so it's not a directory or something else exotic, it's a 'regular' ordinary file. But if it were truly ordinary, that number after the rwx-ish part would be "1", as in, "there is one directory entry pointing to this block of data." But it's part of a demonstration of hard links, so instead it says "3".

Note that this can possibly lead to weird and mysterious behavior (if you haven't wrapped your head around hard links, that is). If you open Fred.txt in your text editor and make some changes, will you see the same changes in Wilma.txt and Barney.txt? Maybe. Probably. If your text editor saves the changes by opening the original file and writing the changes to it, then yes, all three names will still be pointing at the same (newly changed) text. But if your text editor creates a new file (Fred-new-temp.txt), writes your changed version to that, then deletes Fred.txt, then renames Fred-new-temp.txt to Fred.txt, Wilma and Barney will still be pointing to the original version, not the new changed version. If you don't understand hard links, this could drive you slightly mad. :) [Okay, I don't actually personally know of any text editors that would do the new-file/rename thing, but I do know of lots of other programs that do exactly that, so stay alert.]

A final note: one of the things that 'fsck' (file system check) checks for is if there are blocks of data on your drive that somehow are no longer referenced by any directory entries. Sometimes something goes wrong, and the only directory entry that points to an inode gets deleted but the drive space itself doesn't get marked as "available." So one of fsck's jobs is to match up all the allocated space with all the directory entries to make sure that there aren't any unreferenced files. If it finds some, it creates new directory entries and puts them in "lost+found".

  • Just wondering, what are those "other programs that do exactly that"? – phk Sep 3 '16 at 8:45
  • @phk Don't know what he is thinking about specifically, but that is a common enough approach for doing actions which can take a long time and will leave you in an uncertain state if failed. For example, if you try to download from a remote server, and you know there is a chance that the server could timeout, then one approach would be to download the entire contents to a temp file. That way if something goes wrong with the download you still have the original file. – cwallenpoole Nov 11 '16 at 21:00
  • 1
    The only program I know for certain is FreeHand, because if/when it crashes during a save, there's a temporary file left behind, rather than a mangled original file. But I've seen other programs do it too; I just can't give you specific examples at this time. – Snarke Nov 13 '17 at 22:36

This didn't work for me:

    if [ "$(stat -c %h -- "$file")" -gt 1 ]; then
        echo "File has more than one name."

It didn’t work for me, keep it working):

if [ "$1" = "" ]
        echo "Please enter command argument."
links=$(stat -c %h "$1" 2>/dev/null)
inode=$(stat -c %i "$1" 2>/dev/null)
if [ $links -gt 1 ]
        echo "File has hard links, select directory to search...";
            PS3='Please enter your choice: '
            select opt in / /home Quit
    case $opt in
           find / -inum $inode 2>/dev/null
           find /home -inum $inode 2>/dev/null
           echo "Quit"
        *) echo "invalid option $REPLY"


        echo "File has no hard links"
  • welcome to U&L, can you post on this SO forum in english please ? – Archemar Feb 1 at 12:27
  • Certainly can! But I do not see any need for this, since I myself use the translator built into the browser and do not experience any difficulties in understanding! Moreover, as you noticed, the code is written in English. And to translate 2 lines from Russian, if necessary, I think it will not bother anyone. Or do you have a preconceived attitude towards Russian-speaking people? – Андрей Сомов Feb 1 at 18:31
  • No, we simply have a rule that all content posted on this site needs to be in English. The vast majority of users here are not native English speakers, so everyone needs to translate from their language to English. So please make sure to only post in English in the future. – terdon Mar 17 at 11:27
  • I tried to translate but it seems like you are just giving solutions that don't work. Please edit the translation so it makes sense. Of course, your script doesn't actually work since you have a syntax error (if foo; then bar; fi not if foo; then do bar; done fi). – terdon Mar 17 at 11:30

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.