You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
5.5 KiB
Bash
144 lines
5.5 KiB
Bash
#!/bin/sh
|
|
|
|
# Sets REPLY to the absolute, well-formed path of a physical directory that
|
|
# contains (or would contain) the supplied path. (Equivalent to the dirname of
|
|
# realpath.resolved "$1".) Always succeeds.
|
|
realpath_location(){ realpath_follow "$1"; realpath_absolute "$REPLY" ".."; }
|
|
|
|
# Sets REPLY to the absolute, well-formed path of $1, with symlinks in the
|
|
# final path portion resolved. (Equivalent to the realpath.absolute of
|
|
# realpath.follow "$1".) Always succeeds. The result is not a symlink unless it
|
|
# is inaccessible or part of a symlink cycle.
|
|
realpath_resolved(){ realpath_follow "$1"; realpath_absolute "$REPLY"; }
|
|
|
|
realpath_dirname() { REPLY=$(dirname "$1"); }
|
|
realpath_basename() { REPLY=$(basename "$1"); }
|
|
|
|
# Sets REPLY to the first non-symlink (or last accessible, non-looping symlink)
|
|
# in the symlink chain beginning at $1. Replies with the unchanged $1 if it is
|
|
# not a symlink. Always succeeds. The result is not a symlink unless it is
|
|
# inaccessible or part of a symlink cycle. (Note: the result can be a relative
|
|
# path, if both $1 and all the symlinks in the chain are relative. Use
|
|
# realpath.resolved instead if you want a guaranteed-absolute path.)
|
|
realpath_follow() {
|
|
while [ -L "$1" ] && target=$(readlink -- "$1"); do
|
|
realpath_dirname "$1"
|
|
# Resolve relative to symlink's directory
|
|
if [ "$REPLY" = "." ]; then
|
|
REPLY=$target
|
|
else
|
|
case "$target" in
|
|
/*)
|
|
REPLY=$target;;
|
|
*)
|
|
REPLY=$REPLY/$target;;
|
|
esac
|
|
fi
|
|
# Break out if we found a symlink loop
|
|
for target; do [ "$REPLY" = "$target" ] && break 2; done
|
|
# Add to the loop-detect list and tail-recurse
|
|
set -- "$REPLY" "$@"
|
|
done
|
|
REPLY="$1"
|
|
}
|
|
|
|
# Sets REPLY to the absolute, well-formed combination of the supplied path(s).
|
|
# Always succeeds.
|
|
#
|
|
# Each path may be absolute or relative. The resulting path is the combination
|
|
# of the last absolute path in the list supplied, combined with any relative
|
|
# paths that follow it. If no absolute paths are given, the relative paths are
|
|
# processed relative to $PWD -- so passing zero arguments simply returns $PWD.
|
|
#
|
|
# Relative path parts are resolved logically rather than physically. That is to
|
|
# say, .. is processed by removing elements from the path string, rather than
|
|
# by inspecting the filesystem. (So symlinks are not processed in any way, and
|
|
# the existence or accessibility of the files and directories is irrelevant:
|
|
# with the exception of defaulting to $PWD, the result is obtained solely via
|
|
# string manipulation of the supplied paths.)
|
|
#
|
|
# As per POSIX, bash, Python, and other path handling libraries, absolute paths
|
|
# beginning with exactly two slashes are treated specially. The string returned
|
|
# by realpath.absolute will begin with two slashes if the last absolute path
|
|
# argument began with exactly two slashes; otherwise the result will begin with
|
|
# only one slash, no matter how many slashes the last absolute path argument
|
|
# began with.
|
|
realpath_absolute() {
|
|
REPLY=$PWD
|
|
while [ $# -gt 0 ]; do
|
|
if echo "$1" | grep -E '^(//[^/]*)$' >/dev/null 2>&1; then
|
|
REPLY=//
|
|
first=$(echo "$1" | cut -c3-)
|
|
shift
|
|
set -- "$first" "$@"
|
|
continue
|
|
fi
|
|
case $1 in
|
|
/*)
|
|
REPLY=/
|
|
first=$(echo "$1" | sed -e 's/^\/\+//')
|
|
shift
|
|
set -- "$first" "$@"
|
|
;;
|
|
*/*)
|
|
front=${1%%/*}
|
|
back=$(echo "$1" | sed -e "s/^$front\\/\\+//")
|
|
shift
|
|
set -- "$front" "$back" "$@"
|
|
;;
|
|
''|.)
|
|
shift
|
|
;;
|
|
..)
|
|
realpath_dirname "$REPLY"
|
|
shift
|
|
;;
|
|
*) REPLY="${REPLY%/}/$1"; shift ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Sets REPLY to the fully canonicalized form of $1, recursively resolving
|
|
# symlinks in every part of the path where that can be done, roughly equivalent
|
|
# to realpath -m or readlink -m. Always succeeds, but potentially rather slow,
|
|
# depending on how many directories are symlinks.
|
|
#
|
|
# You don't really need this function unless you are trying to determine
|
|
# whether divergent paths lead to the "same" file. For use cases that don't
|
|
# involve comparing paths, realpath.resolved should be sufficient, or perhaps
|
|
# even realpath.absolute. (Note, too, that using canonical paths can result in
|
|
# user confusion, since users then have to reconcile their inputs with your
|
|
# outputs!)
|
|
realpath_canonical() {
|
|
realpath_follow "$1"; set -- "$REPLY" # $1 is now resolved
|
|
realpath_basename "$1"; set -- "$1" "$REPLY" # $2 = basename $1
|
|
realpath_dirname "$1"
|
|
[ "$REPLY" != "$1" ] && realpath_canonical "$REPLY"; # recurse unless root
|
|
realpath_absolute "$REPLY" "$2"; # combine canon parent w/basename
|
|
}
|
|
|
|
# Sets REPLY to the shortest relative path from basedir to path. basedir
|
|
# defaults to $PWD if not supplied. Always succeeds.
|
|
#
|
|
# The path and basedir are preprocessed with realpath.absolute, so they can be
|
|
# relative paths, or absolute ones containing relative components. (The result
|
|
# is identical to calling Python's os.path.relpath function with the same
|
|
# arguments, but is much faster than even the fork to start Python would be.)
|
|
#
|
|
# The main use case for this function is portably creating relative symlinks
|
|
# without needing ln --relative or realpath --relative-to. Specifically, if you
|
|
# are doing ln -s somepath somedir/link, you can make it relative using
|
|
# realpath.relative somepath somedir; ln -s "$REPLY" somedir/link.
|
|
realpath_relative() {
|
|
target=""
|
|
realpath_absolute "$1"
|
|
shift
|
|
set -- "$REPLY" "$@"
|
|
realpath_absolute "${2-$PWD}" X
|
|
while realpath_dirname "$REPLY"; [ "$1" != "$REPLY" ] && [ "$1" = "${1#${REPLY%/}/}" ]; do
|
|
target=../$target
|
|
done
|
|
[ "$1" = "$REPLY" ] && REPLY=${target%/} || REPLY="$target${1#${REPLY%/}/}"
|
|
REPLY=${REPLY:-.}
|
|
}
|