determining path to sourced shell script
In tcsh
, $_
at the beginning of the script will contain the location if the file was sourced and $0
contains it if it was run.
#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
echo "run $0"
endif
In Bash:
#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"
I think that you could use $BASH_SOURCE
variable. It returns path that was executed:
pbm@tauri ~ $ /home/pbm/a.sh
/home/pbm/a.sh
pbm@tauri ~ $ ./a.sh
./a.sh
pbm@tauri ~ $ source /home/pbm/a.sh
/home/pbm/a.sh
pbm@tauri ~ $ source ./a.sh
./a.sh
So in next step we should check if path is relative or not. If it's not relative everything is ok. If it is we could check path with pwd
, concatenate with /
and $BASH_SOURCE
.
This solution applies only to bash and not tcsh. Note that the commonly supplied answer ${BASH_SOURCE[0]}
won't work if you try to find the path from within a function.
I've found this line to always work, regardless of whether the file is being sourced or run as a script.
echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
If you want to follow symlinks use readlink
on the path you get above, recursively or non-recursively.
The double quotes prevent spaces in the paths from splitting the result to an array and are required not only for echo
, but in any context this is used. Since echo
inserts a single space between array items, the difference is hidden unless there are two or more consecutive spaces in the path.
Here's a script to try it out and compare it to other proposed solutions. Invoke it as source test1/test2/test_script.sh
or bash test1/test2/test_script.sh
.
#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo "${BASH_SOURCE}"
echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"
function test_within_func_inside {
echo "${BASH_SOURCE}"
echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
}
echo "Testing within function inside"
test_within_func_inside
echo "Testing within function outside"
test_within_func_outside
#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
echo "${BASH_SOURCE}"
echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
}
The reason the one-liner works is explained by the use of the BASH_SOURCE
environment variable and its associate FUNCNAME
.
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]}.
FUNCNAME
An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.
This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.
[Source: Bash manual]