287

I'm trying to check if a symlink exists in bash. Here's what I've tried.

mda=/usr/mda
if [ ! -L $mda ]; then
  echo "=> File doesn't exist"
fi


mda='/usr/mda'
if [ ! -L $mda ]; then
  echo "=> File doesn't exist"
fi

However, that doesn't work. If '!' is left out, it never triggers. And if '!' is there, it triggers every time.

codeforester
  • 39,467
  • 16
  • 112
  • 140
bear
  • 11,364
  • 26
  • 77
  • 129

8 Answers8

416

-L returns true if the "file" exists and is a symbolic link (the linked file may or may not exist). You want -f (returns true if file exists and is a regular file) or maybe just -e (returns true if file exists regardless of type).

According to the GNU manpage, -h is identical to -L, but according to the BSD manpage, it should not be used:

-h file True if file exists and is a symbolic link. This operator is retained for compatibility with previous versions of this program. Do not rely on its existence; use -L instead.

Soviut
  • 88,194
  • 49
  • 192
  • 260
drysdam
  • 8,341
  • 1
  • 20
  • 23
  • 2
    I'm looking to see if a symlink DOESN'T exist. !-h or !-L should work for symlinks, !-e should work otherwise. – bear Apr 23 '11 at 21:32
  • 57
    To help anyone who finds this via Google as I did, the full syntax using `!` is `if ! [ -L $mda ]; then .... fi` i.e. put the exclamation mark _outside_ the square brackets. – Sam Sep 05 '12 at 08:06
  • 30
    Just wanted to add a little something to the tip given by @Sam; when doing these sorts of operations make sure to put your file name in quotes, to prevent issues with whitespaces. e.g. `if [ ! -L "$mda" ]; then ... fi` (note: `if [ ! ... ]` and `if ! [ ... ]` are identical :) – Thomas Vervest Aug 06 '13 at 12:26
  • 2
    do you really see a difference between -L and -h ? in my bash ( version 4.2.53(1)-release (x86_64-redhat-linux-gnu ) man bash is identical for both -L and -h and they behave the same, ie they check that file actualy is a link and don't care whether the linked to file exists or not. – philippe lhardy Jan 19 '15 at 14:41
  • 4
    Yes, `-L` and `-h` are the [same](https://stackoverflow.com/questions/1997666/difference-between-test-h-and-test-l). `man test` also confirms this. – Sparhawk Feb 09 '15 at 01:20
  • 1
    Anybody has an idea how to check whether the **target file** exists? – Tomáš Zato Apr 21 '15 at 17:10
  • @Sparhawk - any idea why they might be synonymous in the first place? The best reason my googling came up with is "because it's bash" – aaaaaa May 29 '15 at 07:10
  • 2
    @aaaaaa No idea… I thought I remembered some commands where `-h` was to do with symlinks, but I can't seem to find them now. `cp -H` is similar I guess, although mostly `-h` is to show the help. You should ask it as a question (perhaps in Unix/Linux)! – Sparhawk May 29 '15 at 12:41
  • 1
    @aaaaaa `man test` on BSD's `test` provided some insight about `-h`: `This operator is retained for compatibility with previous versions of this program. Do not rely on its existence; use -L instead.` As for why they changed to `-L` over `-h`, I have no idea – SnoringFrog Jan 14 '16 at 20:25
  • 1
    @TomášZato `-e $(readlink -f $symlink)` would check if the linked file exists, but has the caveat that is also return true if `$symlink` exists but is not actually a symlink. – SnoringFrog Jan 14 '16 at 20:41
  • 2
    it seems if a link is broken, -e doesn't work but only -L can tell if the link exist (though the linked file doesn't exist). Is there anyway to test if a file/or even broken link exist ? – dragonxlwang Feb 28 '16 at 20:31
  • 2
    -f also return true if the file is a symbolic link and target is exist. – jolestar Aug 02 '17 at 02:54
  • 1
    @dragonxlwang Just ran into a super annoying bug because of this. This detail should probably be added to the answer to help others in the future. – Jon Anderson Apr 02 '18 at 23:19
99

You can check the existence of a symlink and that it is not broken with:

[ -L ${my_link} ] && [ -e ${my_link} ]

So, the complete solution is:

if [ -L ${my_link} ] ; then
   if [ -e ${my_link} ] ; then
      echo "Good link"
   else
      echo "Broken link"
   fi
elif [ -e ${my_link} ] ; then
   echo "Not a link"
else
   echo "Missing"
fi

-L tests whether there is a symlink, broken or not. By combining with -e you can test whether the link is valid (links to a directory or file), not just whether it exists.

Sinjai
  • 1,085
  • 1
  • 15
  • 34
Chen Levy
  • 15,438
  • 17
  • 74
  • 92
  • 4
    -L test if there is a symlink, broken or not. By combining with -e it is possible to test if the link is also valid (linking to a directory or file). Up-voting this solutions, since I find it important to capture this aspect. – Torbjörn Österdahl Mar 03 '20 at 09:11
  • Thanks ! I'm struggling with the logic behind if, this post really helped me ! +1 – Liso Oct 05 '21 at 06:47
  • bash friendly: `if [[ -L "${my_link}" ]]; then` ... – Jesse Nickles Aug 02 '22 at 10:14
40

-L is the test for file exists and is also a symbolic link

If you do not want to test for the file being a symbolic link, but just test to see if it exists regardless of type (file, directory, socket etc) then use -e

So if file is really file and not just a symbolic link you can do all these tests and get an exit status whose value indicates the error condition.

if [ ! \( -e "${file}" \) ]
then
     echo "%ERROR: file ${file} does not exist!" >&2
     exit 1
elif [ ! \( -f "${file}" \) ]
then
     echo "%ERROR: ${file} is not a file!" >&2
     exit 2
elif [ ! \( -r "${file}" \) ]
then
     echo "%ERROR: file ${file} is not readable!" >&2
     exit 3
elif [ ! \( -s "${file}" \) ]
then
     echo "%ERROR: file ${file} is empty!" >&2
     exit 4
fi
Corin
  • 445
  • 3
  • 3
18

Maybe this is what you are looking for. To check if a file exist and is not a link.

Try this command:

file="/usr/mda" 
[ -f $file ] && [ ! -L $file ] && echo "$file exists and is not a symlink"
Eliot Sykes
  • 9,616
  • 6
  • 50
  • 64
Lynch
  • 9,174
  • 2
  • 23
  • 34
13

How about using readlink?

# if symlink, readlink returns not empty string (the symlink target)
# if string is not empty, test exits w/ 0 (normal)
#
# if non symlink, readlink returns empty string
# if string is empty, test exits w/ 1 (error)
simlink? () {
  test "$(readlink "${1}")";
}

FILE=/usr/mda

if simlink? "${FILE}"; then
  echo $FILE is a symlink
else
  echo $FILE is not a symlink
fi
popedotninja
  • 1,170
  • 1
  • 12
  • 23
5
  1. first you can do with this style:

    mda="/usr/mda"
    if [ ! -L "${mda}" ]; then
      echo "=> File doesn't exist"
    fi
    
  2. if you want to do it in more advanced style you can write it like below:

    #!/bin/bash
    mda="$1"
    if [ -e "$1" ]; then
        if [ ! -L "$1" ]
        then
            echo "you entry is not symlink"
        else
            echo "your entry is symlink"
        fi
    else
      echo "=> File doesn't exist"
    fi
    

the result of above is like:

root@linux:~# ./sym.sh /etc/passwd
you entry is not symlink
root@linux:~# ./sym.sh /usr/mda 
your entry is symlink
root@linux:~# ./sym.sh 
=> File doesn't exist
Amin Shateri
  • 51
  • 1
  • 2
5

Is the file really a symbolic link? If not, the usual test for existence is -r or -e.

See man test.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
4

If you are testing for file existence you want -e not -L. -L tests for a symlink.

Andrew Lazarus
  • 18,205
  • 3
  • 35
  • 53
  • I'm looking to see if a symlink DOESN'T exist. !-h or !-L should work for symlinks, !-e should work otherwise. – bear Apr 23 '11 at 21:32
  • 3
    What you want is not clear. The file exists and is not a symlink? Then test *both* -e and !-h . – Andrew Lazarus Apr 23 '11 at 21:40