# Symbolic links and hard links: creating, updating, deleting

This is a post about understanding linking, symbolic links and hard links.

## Terminology

Symlink, symbolic link, or soft link:

> In computing, a symbolic link (also symlink or soft link) is a special type of
> file that contains a reference to another file or directory in the form of an
> absolute or relative path and that affects pathname resolution.
> —[Wikipedia](https://en.wikipedia.org/wiki/Symbolic_link)

Hard link:

> In computing, a hard link is a directory entry that associates a name with a
> file on a file system. —[Wikipedia](https://en.wikipedia.org/wiki/Hard_link)

## The link

A symlink is like having a directory or file in two places at the same time.

If you execute a file in a path `./file-a` it actually runs the in location
`./file-b`.

## Creating links

Hard link syntax:

```
┌──The link command
│
│                          ┌──Path to the intended link, can use . or ~
│                          │
│                    ┌─────┴──────┐
ln /path/to/original /path/to/link
    └───────┬───────┘
            └──Path to the original file/folder can
               use . or ~ or other relative paths
```

Symlink:

```
┌──The link command
│   ┌──Create a symbolic link
│   │                         ┌── Path to the intended symlink, can use . or ~
│   │                         │
│   │                   ┌─────┴────────┐
ln -s /path/to/original /path/to/symlink
        └───────┬───────┘
                └── the path to the original file/folder
                    can use . or ~ or other relative paths
```

## The difference between soft link and a hard link

Like the name suggest, **soft link** is more "fragile" than a **hard link**.

Here’s stuff one can do with **hard links** that would break a soft link:

- Both ends of hard link can be renamed and the link would persist
- Both ends can be moved around and the link persists
- The source directory of hard link can be deleted and the target link would
  persist

Stuff that soft links do that hard links can’t:

- Reach from a filesystem to another, e.g. from your host machine to a dev VM

Maybe there’s more that I just don’t know, drop a comment if you have something
to add.

### Technical explanation

There are these things called **inodes** (Index Nodes) inside your computer,
which represent files and directories.

> An inode is a data structure on a traditional Unix-style file system such as
> UFS or ext3\. An inode stores basic information about a regular file,
> directory, or other file system object.

A file is really a link to an inode, hard link creates another file with a link
to the same inode.

If you want to test this in practice, you can quickly make some test files with
dummy content:

```shell
$ mkdir link-test \
  && cd link-test \
  && echo Banana > File_A \
  && echo Apple > File_B \
  && echo Orange > File_C \
  && mkdir Dir_A \
  && mv File_C Dir_A/File_C
```

That gives you:

```
├── Dir_A
│   └── File_C
├── File_A
└── File_B
```

You may see the inode number of `File_A`:

```shell
$ ls -i File_A
51882811 File_A
```

The `stat` command shows all the attributes stored into the inode:

```shell {5}
$ stat -x File_A
    File: "File_A"
    Size: 7            FileType: Regular File
    Mode: (0644/-rw-r--r--)         Uid: (  501/   bob)  Gid: (   20/   staff)
Device: 1,4   Inode: 51882811    Links: 1
Access: Tue Jan 20 09:14:04 2015
Modify: Tue Jan 20 09:14:04 2015
Change: Tue Jan 20 09:14:04 2015
```

This is on OS X, the output of `stat` might look a bit different on other
systems.

> 👉 **Related:** [Better stat command](/better-stat-command-in-os-x).

Files can be deleted based on their inode number:

```shell
$ find . -inum 51882811 -exec rm -i {} ;
```

[More here](http://www.cyberciti.biz/tips/delete-remove-files-with-inode-number.html).

### Visual explanation

In hard links the link sits, kind of, between the inode and the file:

```
$ ln File_A File_B

┌──────────┐     ┌──────────┐
│  File_A  |     |  File_B  |
└─────┬────┘     └─────┬────┘
      |                |
      └─────┐  ┌─link──┘
            |  |
      ┌─────┴──┴─────┐
      |    inode     |
      | representing |
      |    File_A    |
      └──────────────┘
```

Whereas a soft link has nothing to do with the inode, it links between the files
like so:

```
$ ln -s File_A File_B

  ┌──────────┐          ┌──────────┐
  |  File_A  ├───link───|  File_B  |
  └─────┬────┘          └──────────┘
        |
        └─────┐
              |
        ┌─────┴────────┐
        |    inode     |
        | representing |
        |    File_A    |
        └──────────────┘
```

Hope I got that right, please correct me in the comments if you have a better
explanation.

## More link syntax examples

If link target file name is not specified, the originals name will be used:

```shell
$ cd link-test
$ ln -s /path/to/File_B .
```

That just grabs the `File_B` from an absolute path and jugs the link into the
current directory.

Any sort of a relative path can be used with links if needed:

```shell
$ ln -s ../../File_B .
```

### Visualizing the links

The test directory has one **soft link** and one **hard link**. The `-l` flag on
the `ls` command shows the symlink locations:

```shell {3}
$ ls -l
-rw-r--r--  1 bob  staff  7 20 Jan 09:14 File_A
lrwxr-xr-x  1 bob  staff  6 21 Jan 10:32 File_A_link -> File_A
-rw-r--r--  2 bob  staff  6 20 Jan 09:14 File_B
-rw-r--r--  2 bob  staff  6 20 Jan 09:14 File_B_link
```

Or the
[more explicit command `readlink`](https://www.man7.org/linux/man-pages/man2/readlink.2.html)
can be used:

```shell
$ readlink File_A_link
File_A
```

Hard links aren’t shown the same way, per se. But notice the number `2` before
the user name on the `ls` command output (line highlighted):

```shell {4-5}
$ ls -l
-rw-r--r--  1 bob  staff  7 20 Jan 09:14 File_A
lrwxr-xr-x  1 bob  staff  6 21 Jan 10:32 File_A_link -> File_A
-rw-r--r--  2 bob  staff  6 20 Jan 09:14 File_B
-rw-r--r--  2 bob  staff  6 20 Jan 09:14 File_B_link
            ^
```

That means **the file has two names**, the original and the link. The duplicate
files can be viewed with `find`:

```shell
$ find . -xdev -samefile File_B
./File_B
./File_B_link
```

Here’s the breakdown of the command:

`.`

Where to search, current directory in this case.

`-xdev`

Don’t descend directories on other filesystems.

`-samefile filename`

True if the file is a hard link to name. If the command option
    `-L` is specified, it is also true if the file is a symbolic link
    and points to a name.

`File_B`

What to search.

### Removing hard links

```shell
$ rm File_B
$ ls
File_A    File_A_link    File_B_link
```

The `File_B_link` persists, essentially it’s now the original `File_B`.

### Remove soft link

Either, remove the link:

```shell
$ rm File_A_link
```

Or use `unlink`:

```shell
$ unlink File_A_link
```

### Remove soft link from a directory

There is a small gotcha here: link the directory, try to remove it, and Bash
will nag you about it:

```shell
$ ln -s Dir_A Dir_A_link
$ unlink Dir_A_link/
unlink: Dir_A_link/: is a directory
```

The catch is the forward slash after the directory name. Leave it out and it
should work:

```shell
$ unlink Dir_A_link
```

### Update a soft link

Now the `File_A_link` links to `File_A`:

```shell
$ ls -l File_A_link
lrwxr-xr-x  1 bob  staff  6 21 Jan 15:09 File_A_link -> File_A
```

Update the link to point to `Dir_A/File_C`:

```shell
$ ln -nsf Dir_A/File_C File_A_link
```

### If symbolic link

Symlink can be tested with an `if` statement. The syntax is:

`[ -h FILE ]` True if FILE exists and is a symbolic link.

Simple example script:

```bash
is_link() {
  local file=$1
  [[ -h $file ]]
}

if is_link path/to/file; then
  # Do something...
fi
```

## Conclusions

That’s the basics of links, thanks for reading. Please leave a comment if you
have something to add.
