Odio continuare con un'altra implementazione, ma avevo bisogno di a) un'implementazione portatile, pure shell , e b) copertura unit-test , in quanto il numero di casi limite per qualcosa di simile non è banale .
Vedi il mio progetto su Github per test e codice completo. Quella che segue è una sinossi dell'implementazione:
Come Keith Smith sottolinea astutamente, readlink -f
fa due cose: 1) risolve ricorsivamente i link simbolici e 2) canonicalizza il risultato, quindi:
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
Innanzitutto, l'implementazione del resolver symlink:
resolve_symlinks() {
local dir_context path
path=$(readlink -- "$1")
if [ $? -eq 0 ]; then
dir_context=$(dirname -- "$1")
resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
else
printf '%s\n' "$1"
fi
}
_prepend_path_if_relative() {
case "$2" in
/* ) printf '%s\n' "$2" ;;
* ) printf '%s\n' "$1/$2" ;;
esac
}
Si noti che questa è una versione leggermente semplificata dell'implementazione completa . L'implementazione completa aggiunge un piccolo controllo per i cicli di collegamento simbolico e massaggia un po 'l'output.
Infine, la funzione per canonicalizzare un percorso:
canonicalize_path() {
if [ -d "$1" ]; then
_canonicalize_dir_path "$1"
else
_canonicalize_file_path "$1"
fi
}
_canonicalize_dir_path() {
(cd "$1" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
local dir file
dir=$(dirname -- "$1")
file=$(basename -- "$1")
(cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}
Questo è tutto, più o meno. Abbastanza semplice da incollare nel tuo script, ma abbastanza complicato da farti impazzire per fare affidamento su qualsiasi codice che non ha unit test per i tuoi casi d'uso.
readlink
può essere un comando incorporato o esterno.