Join my Laravel for REST API's course on Udemy 👀

Check if a file is a symlink with Bash

March 25, 2020  ‐ 5 min read

Your Bash script might need to determine if a file is a symlink or not. In Bash you can test this with the -L operator that returns true if the file exists and is a symlink. Lets find out how this works by making a symchecker script.

#!/bin/bash

# 1. We create a file.
touch ~/script

# 2. Link the created file above to the ~/bin directory.
ln -s ~/script ~/bin/script

# 3. Check if ~/bin/script is a symlink.
if [[ -L "$HOME/bin/script" ]]; then
  echo "It's a link!"
fi
$ bash symchecker
It's a link!

So we see that works. But the script isn't perfect yet. Even though we checked for an existing symlink, this doesn't guarantee that the symlink isn't broken. Meaning the file that was symlinked might not exist anymore.

Before we proceed, lets clean up the symlink we created. Otherwise ln will show an error message the next time we run the script. Because the link already exist.

$ unlink ~/bin/script

Lets see what I mean with broken links. We'll add a step to the symchecker script that removes the initial file but doesn't touch the symlink.

#!/bin/bash

# 1. We create a file.
touch ~/script

# 2. Link the created file above to the ~/bin directory.
ln -s ~/script ~/bin/script

# 3. Remove the initial file.
rm ~/script

# 4. Check if ~/bin/script is a symlink.
if [[ -L "$HOME/bin/script" ]]; then
  echo "It's a link!"
fi
$ bash symchecker
It's a link!

And what do you know. The file where the symlink links to is removed but the test still succeeds. To include this check we use the -e operator which checks if a file exist.

#!/bin/bash

# 1. We create a file.
touch ~/script

# 2. Link the created file above to the ~/bin directory.
ln -s ~/script ~/bin/script

# 3. Remove the initial file.
rm ~/script

# 4. Check if ~/bin/script is a symlink.
if [[ -L "$HOME/bin/script" && -e "$HOME/bin/script" ]]; then
  echo "It's a link!"
else
  echo "Might be a link, but it doesn't exist!"
fi
$ bash symchecker
ln: failed to create symbolic link '/home/koen/bin/script': File exists
Might be a link, but it doesn't exist!

So that did the trick. But uh oh, we didn't unbind the symlink before. Now that we have a check for valid symlinks, we might as well safe us some time and clean up a broken link if we find one.

#!/bin/bash

# 1. We create a file.
touch ~/script

# 2. Link the created file above to the ~/bin directory.
ln -s ~/script ~/bin/script

# 3. Remove the initial file.
rm ~/script

# 4. Check if ~/bin/script is a symlink.
if [[ -L "$HOME/bin/script" ]]; then
  if [[ -e "$HOME/bin/script" ]]; then
    echo "It's a link!"
  else
    unlink ~/bin/script
    echo "Link was broken, I cleaned it up!"
  fi
else
  echo "Not a link!"
fi
$ bash symchecker
ln: failed to create symbolic link '/home/koen/bin/script': File exists
Link was broken, I cleaned it up!

So it look liked it worked! But we still see the error message from ln. Actually this is because the first time we run the script, it was still there from the previous version from the symchecker script. In which we didn't clean up. To double check, we can it a second try.

$ bash symchecker
Link was broken, I cleaned it up!

Yup, that looks better. But... all what we have now is a script that:

  1. Creates a file
  2. Creates a symlink to that file
  3. Removes that file
  4. Removes the symlink

So the end result is basically nothing.

Lets make the script sort of usable by letting it expect a file path as parameter. So we will end up with a script that checks if the specified file is a symlink or not and clean it up if it happens to be broken.

#!/bin/bash

# 1. Store the first parameter to the script in a variable.
path="$1"

# 2. Now that we expect a parameter, lets check if it was provided.
if [[ -z "$path" ]]; then
  echo "Script expects a parameter"
  exit 1
fi

# 3. Check if the parameter is a symlink.
if [[ -L "$path" ]]; then
  # 4. Check if it links to a valid path.
  if [[ -e "$path" ]]; then
    echo "$path is a valid link!"
  else
    unlink "$path"
    echo "Cleaned up broken link: $path"
  fi
else
  echo "$path is not a symlink."
fi
$ bash symchecker ~/brokenlink
Cleaned up broken link: ~/brokenlink

There you go. An advanced symlink checker and broken link cleaner in one. All that's left is putting the script in your PATH and make is executable with chmod.

Learning more

The best way to learn more is to use Bash. A lot. Don't forget that Google is your friend.

In case you learn well from books I would recommend these.