How to get the directory of a running Bash script

Get the absolute directory of a Bash script with dirname, BASH_SOURCE, and readlink -f. Handles symlinks and being called from any working directory.

Tldr

Use `dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"` to get the absolute directory containing the running script, independent of the current working directory. Add `readlink -f` first if the script may be invoked through a symlink.

Intro

Scripts that reference sibling files (configs, other scripts, assets) need a reliable way to find their own location, since $0 and the current working directory are not trustworthy for this.

Steps

Name

Understand why $PWD and $0 alone are not enough

Text

$PWD is the caller's working directory, not the script's location. $0 may be relative ("./script.sh") or just the bare name if the script was sourced, so it cannot be used directly as a path.

Name

Use BASH_SOURCE with dirname and cd

Text

BASH_SOURCE[0] holds the path to the current script file (unlike $0, it works correctly when the script is sourced). Piping it through dirname, then cd + pwd, resolves it to an absolute path.

Name

Resolve symlinks first if needed

Text

If the script may be invoked via a symlink (common for CLI tools installed into /usr/local/bin), resolve the real path first with readlink -f (GNU) or realpath.

Name

Use the directory to reference sibling files

Text

Once you have the absolute directory, build paths to config files or helper scripts relative to it, rather than relative to the caller's working directory.

Name

On macOS, prefer readlink with a fallback

Text

macOS ships BSD readlink, which lacks -f by default. Use greadlink (from coreutils via Homebrew) or a small portable function if you need to support macOS without extra dependencies.

Faq

Q

Why not just use dirname "$0"?

A

$0 is unreliable when the script is sourced (it may show the parent shell's name instead) or invoked via a relative/PATH lookup. BASH_SOURCE[0] is specifically designed to track the current file being executed or sourced.

Q

Does this work if the script is sourced rather than executed?

A

Yes — BASH_SOURCE[0] correctly reflects the sourced file's path, whereas $0 would show the top-level script or interactive shell name instead.

Q

Is there a simpler one-liner for portable scripts?

A

If you can require Bash 4.4+ and GNU coreutils, `dir="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"` is a common compact alternative to the cd+pwd pattern.