2 changed files with 79 additions and 0 deletions
@ -0,0 +1,55 @@ |
|||
#!/bin/sh |
|||
|
|||
# Function by mklement0 |
|||
# https://stackoverflow.com/a/29835459/5309963 |
|||
# The following, POSIX-compliant shell function implements what |
|||
# GNU's readlink -e does and is a reasonably robust solution |
|||
# that only fails in two rare edge cases: |
|||
# - paths with embedded newlines (very rare) |
|||
# - filenames containing literal string -> (also rare) |
|||
|
|||
( # Execute the function in a *subshell* to localize variables and the effect of `cd`. |
|||
|
|||
target=$1 fname= targetDir= CDPATH= |
|||
|
|||
# Try to make the execution environment as predictable as possible: |
|||
# All commands below are invoked via `command`, so we must make sure that `command` |
|||
# itself is not redefined as an alias or shell function. |
|||
# (Note that command is too inconsistent across shells, so we don't use it.) |
|||
# `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have |
|||
# an external utility version of it (e.g, Ubuntu). |
|||
# `command` bypasses aliases and shell functions and also finds builtins |
|||
# in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that |
|||
# to happen. |
|||
{ \unalias command; \unset -f command; } >/dev/null 2>&1 |
|||
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too. |
|||
|
|||
while :; do # Resolve potential symlinks until the ultimate target is found. |
|||
[ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; } |
|||
command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path. |
|||
fname=$(command basename -- "$target") # Extract filename. |
|||
[ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/' |
|||
if [ -L "$fname" ]; then |
|||
# Extract [next] target path, which may be defined |
|||
# *relative* to the symlink's own directory. |
|||
# Note: We parse `ls -l` output to find the symlink target |
|||
# which is the only POSIX-compliant, albeit somewhat fragile, way. |
|||
target=$(command ls -l "$fname") |
|||
target=${target#* -> } |
|||
continue # Resolve [next] symlink target. |
|||
fi |
|||
break # Ultimate target reached. |
|||
done |
|||
targetDir=$(command pwd -P) # Get canonical dir. path |
|||
# Output the ultimate target's canonical path. |
|||
# Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path. |
|||
if [ "$fname" = '.' ]; then |
|||
command printf '%s\n' "${targetDir%/}" |
|||
elif [ "$fname" = '..' ]; then |
|||
# Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied |
|||
# AFTER canonicalization. |
|||
command printf '%s\n' "$(command dirname -- "${targetDir}")" |
|||
else |
|||
command printf '%s\n' "${targetDir%/}/$fname" |
|||
fi |
|||
) |
|||
@ -0,0 +1,24 @@ |
|||
die() { |
|||
# Use notify-send to send errors when not in a terminal |
|||
# [ -t 0 ] only works outside of pipes |
|||
[ -t 0 ] || notify-send "$@" && >&2 echo "$@" |
|||
exit 1 |
|||
} |
|||
|
|||
# Output error to stderr and to graphical notification |
|||
notify_err() { |
|||
tee /dev/fd/2 | xargs -n1 -d "\n" notify-send |
|||
} |
|||
|
|||
assert_exists() { |
|||
for c in $@; do |
|||
which "$c" > /dev/null 2>&1 || die "$c doesn't appear to be installed" |
|||
done |
|||
} |
|||
|
|||
check_exists() { |
|||
for c in $@; do |
|||
which "$c" > /dev/null 2>&1 || return 1 |
|||
done |
|||
} |
|||
|
|||
Loading…
Reference in new issue