Categories
System Administration Technical

Pattern for setting root paths in a bash binary

(2025 – updated to current practices) I find myself constantly seeking to set up a coding pattern where I have a project (say, a web application) which has root directories such as:

  • bin
  • public
  • classes
  • theme
  • vendor/bin
  • etc

etc. where various resources for our application are stored. What is essential is that that sometimes there are multiple versions of an application running on the same server at the same time. You want each application to be configured independently, and you want it to depend only on the codebase it’s a part of.

That said, to do this you set a “marker” in your bash scripts which allow you to refer to other directories from the application root.

The pattern is this, say for a file in bin/test-app.sh:

home="${BASH_SOURCE[0]%/*}/.."

Later in that shell script, I can refer to the resources using the $home variable. You can call realpath on this if needed, or change to that directory if needed.

I’ve tried various techniques to determine the real path of the bash script, the above works consistently and across systems (Ubuntu, Debian, CentOS, and FreeBSD.)

A sample stub script from running PHP Unit tests, for example:

runUnitTests() {
local home="${BASH_SOURCE[0]%/*}/.." binHome
__environment cd $home || return $?
binHome="$home/vendor/bin"
if [ ! -x "$binHome/phpunit" ]; then
_environment "$binHome/phpunit binary is not set up correctly, need to run:" ' composer install && vendor/bin/zesk update' || return $?
fi
"$binHome/phpunit" "$@"
}

To decode this:

"${BASH_SOURCE[0]%/*}/.."
  1. "${BASH_SOURCE[0]}" is bash’s environment variable which represents the current file being interpreted (upon load)
  2. % inside the brackets of a variable name means trim this string on the right using the pattern, in this case /* which means trim everything up to and including the last slash found (e.g. /bash-script.sh)
  3. Note that ${BASH_SOURCE[0]} is populated based on how the script is executed so it may be something like:
    • bash-script.sh or
    • /usr/local/some/path/to/bash-script.sh or more likely something like
    • ../../bin/bash-script.sh
  4. Also note that the value of ${BASH_SOURCE[0]} is only valid during script load time and may not be valid if the current directory changes

So -– if you need to depend on this then it is recommended that the absolute path be stored if needed upon script load.