How a BASH script can find its own location

Most foolproof method I'm aware of is the following:

SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
SCRIPTDIR="${SCRIPTDIR%x}"

or if you're not worried about newlines at the end of your directory names:

SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

How does this work? In the first example you cd into the path portion of the script that was executed (e.g. ./foo of ./foo/bar.sh or /tmp of /tmp/bar.sh) which dirname of ${BASH_SOURCE[0]} provides -- ${BASH_SOURCE[0]} is the top of the stack of source files (e.g. scripts) being called, that is, the current script being executed. Basically this applies the possibly relative pathname used to call the script to the current working directory to get the subshell into the absolute directory path of the script.

After that running pwd returns the current working directory of the subshell, which is now the parent script's location. An "x" is tacked on the end to prevent any trailing carriage returns or line feeds from being lost (why are you storing your script in a directory with a carriage return at the end of its name?). Then by using some BASH parameter expansion tricks the "x" is removed from the end of the SCRIPTDIR variable value.

Just a note, if the script's path involves symlinks SCRIPTDIR will be the path with symlinks (including if the script name is a symlink). If you want the actual non-symlinked path you'll need to do some more work -- unless the version of readlink on your system supports the -f option in which case you're golden.

SCRIPT="${BASH_SOURCE[0]}" # get the location of the script relative to the cwd
while [ -L "$SCRIPT" ]; do # while the filename in $SCRIPT is a symlink
  DIR="$( cd -P "$( dirname "$SCRIPT" )" && pwd )" # similar to above, but -P forces a
                           # change to the physical not symbolic directory
  SCRIPT="$(readlink "$SCRIPT")" # value of symbolic link
   && SCRIPT="$DIR/$SCRIPT" # if $SCRIPT is relative (doesn't begin
                           # with /), resolve relative path where symlink lives
done
DIR="$( cd -P "$( dirname "$SCRIPT" )" && pwd )"

Tags: