Reliable way for a Bash script to get the full path to itself

bash, path

Solution

Here's what I've come up with (edit: plus some tweaks provided by sfstewman, levigroker, Kyle Strand, and Rob Kennedy), that seems to mostly fit my "better" criteria:

SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"

That `SCRIPTPATH` line seems particularly roundabout, but we need it rather than `SCRIPTPATH=`pwd` ` in order to properly handle spaces and symlinks.

The inclusion of output redirection (`>/dev/null 2>&1`) handles the rare(?) case where `cd` might produce output that would interfere with the surrounding `$( ... )` capture. (Such as `cd` being overridden to also `ls` a directory after switching to it.)

Note also that esoteric situations, such as executing a script that isn't coming from a file in an accessible file system at all (which is perfectly possible), is not catered to there (or in any of the other answers I've seen).

The `--` after `cd` and before `"$0"` are in case the directory starts with a `-`.

Problem

I have a Bash script that needs to know its full path. I'm trying to find a broadly-compatible way of doing that without ending up with relative or funky-looking paths. I only need to support Bash, not sh, csh, etc. What I've found so far: The accepted answer to Getting the source directory of a Bash script from within addresses getting the path of the script via `dirname $0`, which is fine, but that may return a relative path (like `.`), which is a problem if you want to change directories in the script and have the path still point to the script's directory. Still, `dirname` will be part of the puzzle. The accepted answer to Bash script absolute path with OS X (OS X specific, but the answer works regardless) gives a function that will test to see if `$0` looks relative and if so will pre-pend `$PWD` to it. But the result can still have relative bits in it (although overall it's absolute) — for instance, if the script is `t` in the directory `/usr/bin` and you're in `/usr` and you type `bin/../bin/t` to run it (yes, that's convoluted), you end up with `/usr/bin/../bin` as the script's directory path. Which works, but... The `readlink` solution on this page, which looks like this: ``` # Absolute path to this script. /home/user/bin/foo.sh SCRIPT=$(readlink -f $0) # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT` ``` But `readlink` isn't POSIX and apparently the solution relies on GNU's `readlink` where BSD's won't work for some reason (I don't have access to a BSD-like system to check). So, various ways of doing it, but they all have their caveats. What would be a better way? Where "better" means: - Gives me the absolute path. - Takes out funky bits even when invoked in a convoluted way (see comment on #2 above). (E.g., at least moderately canonicalizes the path.) - Relies only on Bash-isms or things that are almost certain to be on most popular flavors of *nix systems (GNU/Linux, BSD and BSD-like systems like OS X, etc.). - Avoids calling external programs if possible (e.g., prefers Bash built-ins). - (Updated, thanks for the heads up, wich) It doesn't have to resolve symlinks (in fact, I'd kind of prefer it left them alone, but that's not a requirement).

Original source

Related problems