Je déteste continuer avec une autre implémentation, mais j'avais besoin a) d' une implémentation portable, pure shell , et b) d' une couverture de test unitaire , car le nombre de cas de bord pour quelque chose comme ça n'est pas trivial .
Voir mon projet sur Github pour les tests et le code complet. Ce qui suit est un résumé de la mise en œuvre:
Comme Keith Smith le souligne astucieusement, readlink -f
fait deux choses: 1) résout les liens symboliques récursivement, et 2) canonise le résultat, d'où:
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
Tout d'abord, l'implémentation du résolveur 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
}
Notez qu'il s'agit d'une version légèrement simplifiée de l'implémentation complète . L'implémentation complète ajoute une petite vérification des cycles de liens symboliques , ainsi que masse la sortie un peu.
Enfin, la fonction de canonisation d'un chemin:
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")
}
C'est plus ou moins ça. Assez simple à coller dans votre script, mais assez délicat pour que vous soyez fou de vous fier à n'importe quel code qui n'a pas de tests unitaires pour vos cas d'utilisation.
readlink
peut s'agir d'une commande intégrée ou externe.