How do I get the directory where a Bash script is located from within the script itself?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The standard Bash pattern for getting the directory of the currently running script is a one-liner that combines dirname, BASH_SOURCE[0], cd, and pwd.
This gives you the absolute path to the directory containing the script, regardless of where the caller invoked it from. It matters because scripts that reference sibling files (configs, libraries, data) break when the caller's working directory differs from the script's location. This article explains each piece of the one-liner, covers the symlink edge case, and walks through practical usage patterns.
Breaking Down the One-Liner
Each layer of the command handles a specific problem.
| Component | What It Does | Why It is Needed |
${BASH_SOURCE[0]} | Path to the current script as Bash knows it | More reliable than $0 for sourced scripts |
dirname "..." | Extracts the directory portion of the path | Strips the filename, leaving only the directory |
cd "..." | Changes to that directory in a subshell | Resolves relative paths like ./scripts |
pwd | Prints the absolute working directory | Produces a clean absolute path |
$(...) | Command substitution | Captures the result into SCRIPT_DIR |
The subshell created by $(...) means the cd does not affect the caller's working directory. After this line runs, the shell is still in whatever directory it was before.
Why BASH_SOURCE[0] Instead of $0
Most older examples use $0, and it works in simple cases. But $0 and BASH_SOURCE[0] differ in important situations.
| Invocation Method | $0 | BASH_SOURCE[0] |
./myscript.sh | ./myscript.sh | ./myscript.sh |
bash myscript.sh | myscript.sh | myscript.sh |
source myscript.sh | bash (or -bash) | myscript.sh |
Called from another script via source | Name of the outer script | Name of the sourced file |
When a script is sourced (with source or .), $0 becomes the name of the calling shell or calling script, not the file being sourced. BASH_SOURCE[0] always refers to the file where the line of code lives. For a script that might be both executed and sourced, BASH_SOURCE[0] is the correct choice.
Why dirname Alone is Not Enough
dirname extracts the directory component of a path, but it does not resolve relative paths to absolute ones.
The result ./scripts is relative to the caller's current directory. If any code later changes the working directory (with cd), that relative path becomes invalid. The cd ... && pwd wrapping converts it to an absolute path like /home/user/project/scripts.
A Complete Working Example
Run this from any directory and it will always find its sibling files.
Handling Symbolic Links
The basic pattern resolves the path as Bash sees it. If the script itself is a symlink, BASH_SOURCE[0] contains the symlink path, not the target. To resolve through symlinks to the physical file location, you need a loop.
The -P flag on cd tells it to resolve symlinks in the directory path itself (not just in the script file). The loop follows each symlink hop until it reaches a real file.
When You Need Symlink Resolution
- Scripts installed via package managers that symlink into
/usr/local/bin - Development setups where dotfiles are managed by a symlink farm (stow, dotbot)
- Container images that symlink configuration scripts from shared volumes
If you know your script is never symlinked, the basic one-liner is sufficient and easier to understand.
Using realpath or readlink -f as Alternatives
Some systems provide realpath or readlink -f, which resolve symlinks and produce absolute paths in a single command.
| Command | Availability | Resolves Symlinks | Notes |
cd + dirname + pwd | All Bash systems | No (unless -P and loop) | Most portable |
readlink -f | GNU/Linux | Yes | Not available on older macOS |
realpath | GNU/Linux, macOS 13+ | Yes | May need coreutils on older macOS |
readlink (no -f) | macOS, Linux | One level only | Needs a loop for chained symlinks |
For maximum portability, especially in scripts that run on both macOS and Linux, the cd + dirname + pwd pattern remains the safest choice.
Common Patterns Using SCRIPT_DIR
Loading Configuration
Sourcing Library Files
Referencing Data Files
Setting Up PATH
All of these patterns anchor file references to the script location rather than the caller's working directory, which is the entire point of determining the script directory in the first place.
POSIX sh Compatibility
BASH_SOURCE is a Bash-specific variable. If your script needs to run under plain POSIX sh (dash, ash, busybox sh), you must fall back to $0.
This works for directly executed scripts but breaks when the script is sourced. If POSIX sh compatibility and sourcing support are both required, there is no fully portable solution. In practice, most scripts that need BASH_SOURCE should declare #!/usr/bin/env bash and require Bash.
Common Pitfalls
Using $0 in a script that might be sourced. When a script is sourced, $0 points to the parent shell, not the script file. Use BASH_SOURCE[0] in Bash scripts.
Using dirname without cd + pwd. The result may be a relative path like ./scripts or ../tools, which becomes invalid if the working directory changes later in the script.
Assuming the basic pattern resolves symlinks. It does not. If the script is a symlink to another location, BASH_SOURCE[0] contains the symlink path. Add explicit symlink resolution only when needed.
Forgetting to quote the path. Directories with spaces (like /home/user/my project/scripts) break without proper quoting. Always use "${BASH_SOURCE[0]}" with double quotes.
Using BASH_SOURCE in POSIX sh. The variable does not exist in sh. The script will either fail or produce an empty string. Check your shebang line.
Using pwd without cd first. Running pwd alone gives the caller's working directory, not the script's directory. The cd step is essential.
Summary
- The standard pattern is
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)". BASH_SOURCE[0]is safer than$0because it works correctly even when the script is sourced.- The
cd + pwdstep converts potentially relative paths into stable absolute paths. - Add symlink resolution (a
readlinkloop orreadlink -f) only when the script might be invoked through a symbolic link. - Use
SCRIPT_DIRto build paths to sibling files, configs, and libraries instead of relying on the caller's working directory. - For POSIX
shcompatibility, fall back to$0, but accept the sourcing limitation.

