Take the 2-minute tour ×

Given an absolute or relative path (in a Unix-like system), I would like to determine the full path of the target after resolving any intermediate symlinks. Bonus points for also resolving ~username notation at the same time.

If the target is a directory, it might be possible to chdir() into the directory and then call getcwd(), but I really want to do this from a shell script rather than writing a C helper. Unfortunately, shells have a tendency to try to hide the existence of symlinks from the user (this is bash on OS X):

$ ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd

What I want is a function resolve() such that when executed from the tmp directory in the above example, resolve("foo") == "/Users/greg/tmp/bar".

share|improve this question

10 Answers 10

up vote 52 down vote accepted

According to the standards, pwd -P should return the path with symlinks resolved.

C function char *getcwd(char *buf, size_t size) from unistd.h should have the same behaviour.

getcwd pwd

share|improve this answer
This just works for (the current) directory? If the target is a file, it will not give anything... –  dala Apr 28 '10 at 9:18
Doesn't work if the link is broken as you cannot make it your current path –  Tom Howard Nov 16 '11 at 20:17
readlink -f $path

Note since GNU coreutils 8.15 (2012-01-06), there is a realpath program available that is less obtuse and more flexible than the above. It's also compatible with the FreeBSD util of the same name. It also includes functionality to generate a relative path between two files.

realpath $path

[Admin addition below from comment by halloleodanorton]

For Mac OS X (through at least 10.8.x), use readlink without the -f option:

readlink $path
share|improve this answer
awesome, exactly what I needed! –  rampion Jul 17 '09 at 18:44
This doesn't work in Mac OS X - see stackoverflow.com/questions/1055671/… –  Bkkbrad Aug 23 '09 at 20:19
shame about OS X incompatibility, otherwise nice +1 –  jkp Nov 29 '11 at 8:16
On OS X you can install coreutils with homebrew. It installs it as "grealpath". –  Kief Oct 18 '12 at 14:39
readlink works on OSX, but needs another syntax: readlink $path without the -f. –  halloleo Jan 28 '13 at 21:00

"pwd -P" seems to work if you just want the directory, but if for some reason you want the name of the actual executable I don't think that helps. Here's my solution:


# get the absolute path of the executable
SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")

# resolve symlinks
while [ -h $SELF_PATH ]; do
    # 1) cd to directory of the symlink
    # 2) cd to the directory of where the symlink points
    # 3) get the pwd
    # 4) append the basename
    DIR=$(dirname -- "$SELF_PATH")
    SYM=$(readlink $SELF_PATH)
    SELF_PATH=$(cd $DIR && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
share|improve this answer

One of my favorites is realpath foo

realpath - return the canonicalized absolute pathname

realpath  expands  all  symbolic  links  and resolves references to '/./', '/../' and extra '/' characters in the null terminated string named by path and
       stores the canonicalized absolute pathname in the buffer of size PATH_MAX named by resolved_path.  The resulting path will have no symbolic link, '/./' or
       '/../' components.
share|improve this answer
On Debian (etch and later) this command is available in the realpath package. –  Phil Ross Jun 25 '09 at 11:39
realpath is now (Jan 2012) part of coreutils and backwards compatible with the debian and BSD variant –  pixelbeat Apr 16 '12 at 7:43
I don't have realpath on Centos 6 with GNU coreutils 8.4.31. I've come across several others on Unix & Linux that have a GNU coreutils packaged without realpath. So it seems to be dependent on more than just version. –  toxalot Mar 18 at 22:43

Common shell scripts often have to find their "home" directory even if they are invoked as a symlink. The script thus have to find their "real" position from just $0.

cat `mvn`

on my system prints a script containing the following, which should be a good hint at what you need.

if [ -z "$M2_HOME" ] ; then
  ## resolve links - $0 may be a link to maven's home

  # need this for relative symlinks
  while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
      PRG="`dirname "$PRG"`/$link"


  M2_HOME=`dirname "$PRG"`/..

  # make it fully qualified
  M2_HOME=`cd "$M2_HOME" && pwd`
share|improve this answer

Another way:

# Gets the real path of a link, following all links

# Returns the absolute path to a command, maybe in $PATH (which) or not. If not found, returns the same

# Returns the realpath of a called command.
share|improve this answer

Try this:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
share|improve this answer
function realpath {
    local r=$1; local t=$(readlink $r)
    while [ $t ]; do
        r=$(cd $(dirname $r) && cd $(dirname $t) && pwd -P)/$(basename $t)
        t=$(readlink $r)
    echo $r

#example usage
SCRIPT_PARENT_DIR=$(dirname $(realpath "$0"))/..
share|improve this answer

To workaround the Mac incompatibility I came with

echo `php -r "echo realpath('foo');"`

Not great but cross OS

share|improve this answer

Since I've run into this many times over the years, and this time around I needed a pure bash portable version that I could use on OSX and linux, I went ahead and wrote one:

The living version lives here:


but for the sake of SO, here's the current version (I feel it's well tested..but I'm open to feedback!)

Might not be difficult to make it work for plain bourne shell (sh), but I didn't try...I like $FUNCNAME too much. :)


resolve_path() {
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
        elif [[ -f $npath ]]
        #the link points to a file.
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
        elif [[ -d $npath ]]
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        if ! [[ -e "$opath" ]]
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        elif [[ -d "$opath" ]]
            cd "$opath"
            ndir=$(pwd -P)
        elif [[ -f "$opath" ]]
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            return 1
    #now assemble our output
    echo -n "$ndir"
    if [[ "x${nbase:=}" != "x" ]]
        echo "/$nbase"
    #now return to where we were
    cd "$owd"
    return $retval

here's a classic example, thanks to brew:

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

use this function and it will return the -real- path:

%% cat test.sh
. resolve_path.inc
echo "relative symlinked path:"
which mvn
echo "and the real path:"
resolve_path `which mvn`

%% test.sh

relative symlinked path:

and the real path:
share|improve this answer

Your Answer


