When you source a script using . script.sh or source script.sh, the $0 variable doesn’t point to the sourced script—it stays as the path of the parent shell or calling script. This creates a problem if you need the sourced script to know its own location.
The solution: use ${BASH_SOURCE[0]}
The ${BASH_SOURCE[0]} variable holds the path of the currently executing script, even when sourced. This is the reliable way to reference a script’s own location from within the script itself.
#!/bin/bash
# This works whether the script is sourced or executed directly
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "Script location: $script_dir"
Understanding ${BASH_SOURCE[0]} vs $0
$0— the name or path used to invoke the shell (stays constant from the parent shell)${BASH_SOURCE[0]}— the name of the currently executing script (changes in each script file)
When you source a script:
# In parent.sh
. ./helper.sh
Inside helper.sh:
$0would beparent.shor whatever invoked the parent${BASH_SOURCE[0]}would behelper.sh
${BASH_SOURCE[@]} — the call stack
For more complex scenarios with multiple levels of sourcing, use the array form ${BASH_SOURCE[@]}:
#!/bin/bash
# Print the entire sourcing call stack
for i in "${!BASH_SOURCE[@]}"; do
echo "Level $i: ${BASH_SOURCE[$i]}"
done
This shows the chain of scripts that led to the current execution.
Practical example: sourced config loader
#!/bin/bash
# config.sh - a sourced configuration file
# Get the directory where this config file lives
config_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Load additional files relative to the config location
source "$config_dir/defaults.conf"
source "$config_dir/local.conf" 2>/dev/null || true
export CONFIG_HOME="$config_dir"
The cd and pwd combination handles relative paths correctly, even if the script is sourced from a different working directory.
Getting just the filename (not the full path)
If you need only the script’s name without the directory:
script_name="${BASH_SOURCE[0]##*/}"
echo "Script name: $script_name"
Or for the full path:
script_path="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
Compatibility note
BASH_SOURCE is a Bash-specific feature and won’t work in POSIX sh. If you need portability to other shells, you’ll need to pass the script path as a parameter or use other workarounds. For most modern systems where Bash is standard, ${BASH_SOURCE[0]} is the right choice.
