Skip to content

Instantly share code, notes, and snippets.

@jefferys
Created December 28, 2020 20:46
Show Gist options
  • Save jefferys/4bc5343d9365d8a061517255f8c72730 to your computer and use it in GitHub Desktop.
Save jefferys/4bc5343d9365d8a061517255f8c72730 to your computer and use it in GitHub Desktop.
Get the directory of a path. Example use is to get a bash script's location for loading other bash scripts as modules.
#!/usr/bin/env bash
# Prints error messages to stderr
# @PARAM Concatenates parameters with IFS (default space) to form an error
# message.
# @OUT Prints error message preceeded by "ERROR: " to standard error,
# translating escapes.
# @REQUIRES: echo -e
function sayErr() {
echo -e "ERROR: $@" >&2
}
# Figure out the directory this script is in and return it as $__
# @PARAM $1 - path to obtain directory to. May be absolute or relative
# and may be or contain links
# @RET directory of "$1" as absolute path, in $__. May contain or be a link.
# @IO Error 1 if $1 path does not exist
# @IO Error 1 if $1 path is a link but can not be resolved due to a missing
# target or cyclical links.
# @REQUIRES: readlink, cd -P, dirname, pwd
function getMyDir() {
local path="$1"
if [[ ! -e $path && ! -L $path ]]; then
sayErr "Does not exist: \"$path\"."
return 1
fi
# Resolve source path that is (ends in) in a symlink. Not using
# readlink -f as that does not work on macs. Don't care about links in path
local count=1000
local realPath="$path"
while [[ -L "$realPath" && $count -gt 0 ]]; do
# Handle relative paths to link, need to resolve in context.
if [[ $realPath != /* ]]; then
local dir="$( dirname "$realPath" )"
realPath="$dir/$realPath"
fi
realPath="$(readlink "$realPath")"
(( count-=1 ))
done
if [[ ! -e $realPath ]]; then
sayErr "Path \"$path\" resolves to missing link: \"$realPath\"."
return 1
fi
if [[ $count == 0 ]]; then
sayErr "Too many links, probably cyclical. Can't resolve \"$path\"."
return 1
fi
# Resolve relative path and return.
# With IO capture, > dev/null needed to dump possible unwanted output.
__="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
}
# Main entry point; called if executed
function main() {
getMyDir "$BASH_SOURCE" || exit $!
echo "$__"
}
# If this script is sourced, don't run main.
# Nothing should follow this, the exit status of main should
# be the exit status of the script.
[[ "$0" == "$BASH_SOURCE" ]] && main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment