Skip to content

Instantly share code, notes, and snippets.

@kckrinke
Created March 24, 2017 03:11
Show Gist options
  • Save kckrinke/c4fb9b25b3c81bf46f9a399f51c552a2 to your computer and use it in GitHub Desktop.
Save kckrinke/c4fb9b25b3c81bf46f9a399f51c552a2 to your computer and use it in GitHub Desktop.
Yes I know realpath exists, but it's not installed by default and sometimes it's easier to tuck a script somewhere convenient...
#!/bin/bash
###############################################################################
# fullpath.sh - Find the absolute path to the given file or directory.
#
# updated: 2017-03-23
# created: 2005-05-25
# author: Kevin C. Krinke <kevin@krinke.ca>
# license: LGPL-2.1
###############################################################################
#
# Global Variables
#
SCRIPT_VERSION=1.0
SCRIPT_NAME=$(basename "$0")
V_NORMAL=1
V_QUIET=0
V_DEBUG=2
G_VERBOSITY=$V_NORMAL
G_NULL_TERMINATE=0
declare -A TARGET_PATHS
#
# Helper Functions
#
function STDOUT () { while [ $# -gt 0 ]; do echo "$1"; shift; done; }
function STDERR () { while [ $# -gt 0 ]; do echo "$1" 1>&2; shift; done; }
function ERROR () { while [ $# -gt 0 ]; do STDERR "ERROR: $1"; shift; done; }
function DEBUG () { [ $G_VERBOSITY -ge $V_DEBUG ] && STDERR "$@"; }
function NOTIFY () { [ $G_VERBOSITY -ge $V_NORMAL ] && STDOUT "$@"; }
function DIE () { ERROR "$@"; exit 1; }
function display_usage() {
[ $# -gt 0 ] && ERROR "$@"
STDERR "usage: ${SCRIPT_NAME} [options] <path>"
STDERR ""
STDERR "options:"
STDERR " -h --help this helpful screen"
STDERR " -v --verbose increase verbosity"
#STDERR " -q --quiet silence normal output"
STDERR " -z --null null terminated instead of newlines"
exit 1
}
export EXPANDED_PATH=
function expand_abspath() {
this_path=$(echo "$1" | perl -pe "s#^\./#$(pwd)/#;s#^([^/])#${PWD}/\1#;")
root_path=$(echo "${this_path}" | perl -pe 's/^\///')
declare -a all_pieces
typeset -i I
I=0
orig_ifs="${IFS}"
IFS='/'
for piece in $root_path
do
all_pieces[$I]="${piece}"
I=$((I+1))
done
IFS="${orig_ifs}"
full_path=""
for piece in "${all_pieces[@]}"
do
[ "${piece}" == "." ] && continue
if [ "${piece}" == ".." ]
then
pushd "${full_path}/.." 2>&1 > /dev/null
full_piece=$(pwd -P)
popd 2>&1 > /dev/null
DEBUG "${full_piece} is a relative parent (..)"
full_path="${full_piece}"
continue
fi
full_piece="${full_path}/${piece}"
if [ -L "${full_piece}" ]
then
if [ -d "${full_piece}" ]
then
pushd "${full_piece}" 2>&1 > /dev/null
link_path=$(pwd -P)
popd 2>&1 > /dev/null
DEBUG "${full_piece} is a path symlink to ${link_path}"
full_path="${link_path}"
else
link_path=$(stat --format="%N" "${full_piece}" \
| perl -pe 's!^.+?\-\> ‘(.+?)’\s*$!$1!;' )
DEBUG "${full_piece} is a file symlink to ${link_path}"
full_path="${link_path}"
fi
continue
fi
if [ -d "${full_piece}" ]
then
full_path="${full_piece}"
DEBUG "${full_path} is a directory"
continue
fi
if [ -f "${full_piece}" ]
then
full_path="${full_piece}"
DEBUG "${full_path} is a file"
continue
fi
# from this point forward, things don't exist
DEBUG "${full_piece} does not exist"
full_path="${full_piece}"
done
export EXPANDED_PATH="${full_path}"
}
declare -a OUTPUT_QUEUE
function queue_output() {
IDX=${#OUTPUT_QUEUE[@]}
OUTPUT_QUEUE[${IDX}]="$1"
}
function output_queue() {
for item in "${OUTPUT_QUEUE[@]}"
do
if [ $G_NULL_TERMINATE -eq 1 ]
then
printf '%s%b' "${item}" '\0'
else
echo "${item}"
fi
done
}
#
# Main Function
#
function main() {
typeset -i I
for ((I=0; I<${#TARGET_PATHS[@]}; I++))
do
this_path="${TARGET_PATHS[${I}]}"
DEBUG "Processing: ${this_path}"
expand_abspath "${this_path}"
queue_output "${EXPANDED_PATH}"
done
output_queue
return 0
}
#
# Command Line Processing
#
IDX=0
while [ $# -gt 0 ]
do
OPTION="$1"
OPTARG="$2"
case "${OPTION}" in
"-z"|"--null") G_NULL_TERMINATE=1;;
#"-q"|"--quiet") G_VERBOSITY=$V_QUIET;;
"-v"|"--verbose") G_VERBOSITY=$((G_VERBOSITY+1));;
"-h"|"--help") display_usage;;
*)
# options start with a '-'
echo "${OPTION}" | egrep -q '^\s*\-'
[ $? -eq 0 ] && display_usage "Unknown option \"${OPTION}\""
# assume everything else is the path?
TARGET_PATHS[$IDX]=${OPTION}
IDX=$((IDX+1))
;;
esac
shift
done
# Kickoff
main ; exit $?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment