For Python, see my other answer here.
For Bash, see below:
Summary:
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
# OR, if you do NOT need it to work for **sourced** scripts too:
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# OR, depending on which path you want, in case of nested `source` calls
# FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[0]}")"
# OR, add `-s` to NOT expand symlinks in the path:
# FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"
Details:
How to obtain the full file path, full directory, and base filename of any script being run OR sourced...
...even when the called script is called from within another bash function or script, or when nested sourcing is being used!
For many cases, all you need to acquire is the full path to the script you just called. This can be easily accomplished using realpath
. Note that realpath
is part of GNU coreutils. If you don't have it already installed (it comes default on Ubuntu), you can install it with sudo apt update && sudo apt install coreutils
.
get_script_path.sh (for the latest version of this script, see get_script_path.sh in my eRCaGuy_hello_world repo):
#!/bin/bash
# A. Obtain the full path, and expand (walk down) symbolic links
# A.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# A.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
# B.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "$0")"
# B.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "${BASH_SOURCE[-1]}")"
# You can then also get the full path to the directory, and the base
# filename, like this:
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"
# Now print it all out
echo "FULL_PATH_TO_SCRIPT = \"$FULL_PATH_TO_SCRIPT\""
echo "SCRIPT_DIRECTORY = \"$SCRIPT_DIRECTORY\""
echo "SCRIPT_FILENAME = \"$SCRIPT_FILENAME\""
IMPORTANT note on nested source
calls: if "${BASH_SOURCE[-1]}"
above doesn't give you quite what you want, try using "${BASH_SOURCE[0]}"
instead. The first (0
) index gives you the first entry in the array, and the last (-1
) index gives you the last last entry in the array. Depending on what it is you're after, you may actually want the first entry. I discovered this to be the case when I sourced ~/.bashrc
with . ~/.bashrc
, which sourced ~/.bash_aliases
with . ~/.bash_aliases
, and I wanted the realpath
(with expanded symlinks) to the ~/.bash_aliases
file, NOT to the ~/.bashrc
file. Since these are nested source
calls, using "${BASH_SOURCE[0]}"
gave me what I wanted: the expanded path to ~/.bash_aliases
! Using "${BASH_SOURCE[-1]}"
, however, gave me what I did not want: the expanded path to ~/.bashrc
.
Example command and output:
- Running the script:
~/GS/dev/eRCaGuy_hello_world/bash$ ./get_script_path.sh
FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
SCRIPT_DIRECTORY = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
SCRIPT_FILENAME = "get_script_path.sh"
- Sourcing the script with
. get_script_path.sh
or source get_script_path.sh
(the result is the exact same as above because I used "${BASH_SOURCE[-1]}"
in the script instead of "$0"
):
~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh
FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
SCRIPT_DIRECTORY = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
SCRIPT_FILENAME = "get_script_path.sh"
If you use "$0"
in the script instead of "${BASH_SOURCE[-1]}"
, you'll get the same output as above when running the script, but this undesired output instead when sourcing the script:
~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh
FULL_PATH_TO_SCRIPT = "/bin/bash"
SCRIPT_DIRECTORY = "/bin"
SCRIPT_FILENAME = "bash"
And, apparently if you use "$BASH_SOURCE"
instead of "${BASH_SOURCE[-1]}"
, it will not work if the script is called from within another bash function. So, using "${BASH_SOURCE[-1]}"
is therefore the best way to do it, as it solves both of these problems! See the references below.
Difference between realpath
and realpath -s
:
Note that realpath
also successfully walks down symbolic links to determine and point to their targets rather than pointing to the symbolic link. If you do NOT want this behavior (sometimes I don't), then add -s
to the realpath
command above, making that line look like this instead:
# Obtain the full path, but do NOT expand (walk down) symbolic links; in
# other words: **keep** the symlinks as part of the path!
FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"
This way, symbolic links are NOT expanded. Rather, they are left as-is, as symbolic links in the full path.
The code above is now part of my eRCaGuy_hello_world repo in this file here: bash/get_script_path.sh. Reference and run this file for full examples both with and withOUT symlinks in the paths. See the bottom of the file for example output in both cases.
References:
- How to retrieve absolute path given relative
- taught me about the
BASH_SOURCE
variable: Unix & Linux: determining path to sourced shell script
- taught me that
BASH_SOURCE
is actually an array, and we want the last element from it for it to work as expected inside a function (hence why I used "${BASH_SOURCE[-1]}"
in my code here): Unix & Linux: determining path to sourced shell script
man bash
--> search for BASH_SOURCE
:
BASH_SOURCE
An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME
array variable are defined. The shell function ${FUNCNAME[$i]}
is defined in the file ${BASH_SOURCE[$i]}
and called from ${BASH_SOURCE[$i+1]}
.
See also:
- My answer for Python: How do I get the path and name of the python file that is currently executing?
- [my answer] Unix & Linux: determining path to sourced shell script
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
- and remove it without a command substitution -DIR="${DIR%x}"
.mkdir $'\n'
.