Determining the absolute absolute path of a shell script

In the course of working on projects like server-side Objective-J, jack, and now narwhal, I’ve often had to write shell scripts that needed to know their location in the filesystem. Rather than hardcoding it, I prefer to infer it automatically at runtime. Unfortunately this isn’t as easy as you would expect.

If the script is invoked with an absolute path (“/foo/bar/baz”) or from your PATH (“baz”), then “$0” in the script will contain the absolute of the script (“/foo/bar/baz”). However, if it is invoked using a relative path (“./bar/baz” from “/foo”) then $0 will contain the relative path (“./bar/baz”). Furthermore, if the path to the script is actually a symbolic link, you’ll get the symlink’s path instead of the original.

Surprisingly, I couldn’t find a definitive solution that handles all these cases, so I took the various ones I did find and created one which I think handles all the cases I’m aware of:

If you don’t want to resolve the symlinks remove the second half.

  • http://purl.org/net/mkhl Martin Kühl

    The readlink(1) from GNU coreutils (greadlink(1) when installed via MacPorts) provides this functionality as `greadlink -f`.
    If you don’t mind invoking Python, that provides the same functionality (system-independently) in the function `os.path.abspath`.
    I’m sure perl and ruby have similar functions somewhere.

    • http://tlrobinson.net/ tlrobinson

      I noticed the OS X readlink doesn’t support any of the options the GNU one does. I wanted a solution that works on most Unixy platforms (I’ve tested it on OS X 10.5 and a recent Ubuntu) so it couldn’t rely on features that aren’t standard.

  • Mike

    [code]
    #!/bin/sh

    P=`pwd`
    cmd=`echo $0 | sed -e ‘s#^./##’`

    case $cmd in
    /*) ;;
    *) cmd=$P/$cmd;;
    esac

    script_name=””
    install_root_bin=””

    while
    install_root_bin=`expr
    $cmd’/’ : ‘(/)[^/]*/$’
    | $cmd’/’ : ‘(.*[^/])//*[^/][^/]*//*$’
    | .`
    l=`ls -ld $cmd | grep ‘^[Ll]’`
    [ -n “$l” ]
    do
    m=`expr “$l” : ‘.* (.*)’`
    script_name=`expr “$cmd” : ‘.*/([^/]*)$’`
    case $m in
    /*) cmd=$m ;;
    *) cmd=$synopsys_install_root_bin/$m ;;
    esac
    done

    echo $cmd
    [/code]

    Works on HP-UX, Solaris, Linux, OSX. Always returns the path/name of the script even if linked with a symlink.