Linux / Commande realpath : résoudre un chemin et choisir les bonnes options

realpath transforme un chemin en chemin canonique, généralement absolu, en traitant les composants ., .., les séparateurs superflus et les liens symboliques selon les options choisies. Sur GNU, utilisez -e pour exiger l’existence de toute la cible, ou -m pour calculer un chemin qui peut encore manquer ; le résultat ne crée aucun fichier et ne constitue pas une autorisation d’accès.

Ce guide vise GNU Coreutils sur Linux. Vérifiez realpath --version et realpath --help sur votre machine : l’option explicite -E des versions récentes, les formes relatives et les autres extensions ne sont pas disponibles dans toutes les versions ou implémentations Unix.

Comprendre ce que le chemin canonique représente

realpath -e -- .
realpath -e -- /etc/passwd

La première commande donne le répertoire courant résolu. La seconde donne la cible canonique de /etc/passwd si elle existe et si les composants nécessaires sont accessibles. Le résultat peut différer du texte d’entrée si un composant est un lien symbolique.

Un chemin relatif est interprété à partir du répertoire de travail du processus, pas automatiquement depuis le dossier du script. Pour cette seule position courante, pwd -P et les règles de pwd dans Bash sont plus directs ; realpath accepte aussi d’autres chemins.

Deux chemins canoniques identiques indiquent une résolution vers le même nom au moment des observations. Des chemins différents ne prouvent pas que les fichiers sont distincts : deux liens durs peuvent désigner le même inode. Pour étudier cette identité, distinguez le périphérique et l’inode avec les informations de stat, sans supposer que des observations séparées figent un système de fichiers en cours de modification.

Choisir les conditions d’existence

Choix GNU Condition Usage courant
-e, --canonicalize-existing Tous les composants doivent exister ; un séparateur final exige un répertoire. Examiner un fichier ou dossier existant
-E, --canonicalize, sur une version qui les propose Le dernier composant du chemin résolu peut manquer ; le préfixe doit être un répertoire existant. Préparer le nom d’un nouveau fichier dans un parent existant
-m, --canonicalize-missing Les composants manquants ou indisponibles sont traités comme des répertoires. Calculer un nom projeté, sans prouver sa validité sur le disque

Le comportement GNU sans option d’existence permet déjà au dernier composant de manquer. La forme -E rend ce choix explicite sur les implémentations qui la supportent. La spécification POSIX de realpath demande aux applications portables de choisir -e ou -E plutôt que de supposer un même comportement par défaut partout. Un GNU plus ancien peut appliquer le comportement par défaut tout en refusant l’option -E ; contrôlez l’aide locale avant d’adapter un script.

Essayez les conditions dans un nouvel espace temporaire. Ces commandes créent un petit fichier ordinaire, mais le calcul de chemin ne crée ni nouveau.txt ni le dossier absent :

espace=$(mktemp -d) || exit 1
mkdir -p -- "$espace/existant"
printf 'ABC\n' > "$espace/existant/config.ini"
printf 'Espace de travail : %s\n' "$espace"

realpath -e -- "$espace/existant/./config.ini"
realpath -- "$espace/existant/nouveau.txt"
realpath -m -- "$espace/absent/futur.txt"
test ! -e "$espace/absent" && printf '%s\n' 'Le calcul n’a créé aucun répertoire' 

En remplaçant le préfixe par l’espace temporaire affiché, les trois chemins finissent respectivement par /existant/config.ini, /existant/nouveau.txt et /absent/futur.txt. La dernière ligne confirme l’absence du dossier projeté.

realpath -e -- "$espace/existant/nouveau.txt"
realpath -- "$espace/absent/futur.txt"

Ces deux commandes doivent échouer : la première exige le fichier final absent ; la seconde ne permet pas au dossier intermédiaire absent de manquer. Exécutez-les séparément si votre shell est configuré pour s’arrêter à la première erreur. Pour le cas du nouveau fichier sous le parent existant, une version proposant -E accepte aussi :

realpath -E -- "$espace/existant/nouveau.txt"

Le dernier nom à considérer est celui qui reste après résolution des liens. Un lien cassé vers un nom sous un parent existant et un lien vers une arborescence dont le parent manque ne satisfont donc pas nécessairement les mêmes conditions.

Comprendre -P, -L et -s avec les liens

GNU utilise le mode physique -P par défaut : un lien est résolu avant le traitement d’un .. qui le suit. Avec -L, le traitement logique des composants .. précède la résolution des liens restants. Le mode logique résout donc encore des liens ; il ne signifie pas « ne suivre aucun lien ».

Sur Linux, créez un nouveau petit espace de démonstration avec un véritable lien symbolique. Si ln -s échoue, arrêtez l’essai ; ne remplacez pas le lien par une copie de répertoire pour interpréter les résultats.

espace_liens=$(mktemp -d) || exit 1
mkdir -p -- "$espace_liens/releases/v1"
ln -s -- releases/v1 "$espace_liens/courant" || exit 1

realpath -e -P -- "$espace_liens/courant/.."
realpath -e -L -- "$espace_liens/courant/.."
realpath -m -s -- "$espace_liens/courant/../partage"

Le résultat physique finit par /releases, car courant désigne releases/v1. Le résultat logique est l’espace de démonstration lui-même. Enfin, -s -m calcule un chemin finissant par /partage sans suivre courant et sans exiger ce futur dossier.

-s, aussi nommé --strip ou --no-symlinks, retire les composants de ponctuation du chemin sans développer les liens. Associé à -m, il permet un calcul purement lexical ; le résultat peut alors décrire un autre emplacement que la résolution physique. Réservez cette combinaison au calcul de chaînes lorsque c’est bien le sens voulu.

Produire un chemin relatif à une base

--relative-to choisit le répertoire depuis lequel exprimer le résultat. Cela ne signifie pas que la cible est nécessairement située en dessous : un résultat peut commencer par ...

realpath -m -s --relative-to=/srv/www -- /srv/www/assets/css/site.css
realpath -m -s --relative-to=/srv/www -- /srv/logs/app.log

Ces calculs lexicaux donnent assets/css/site.css et ../logs/app.log. Ils n’annoncent pas l’existence de ces fichiers. Remplacez le calcul -m -s par une résolution adaptée si vous voulez tenir compte des vrais liens et des conditions d’existence.

--relative-base fournit un choix de présentation : les cibles sous la base sont relatives, les autres restent absolues. Il ne filtre pas les cibles extérieures.

realpath -m -s --relative-base=/srv/www -- /srv/www/index.html /var/log/syslog

Le résultat est index.html puis /var/log/syslog. En combinant les deux options, --relative-to définit l’origine du chemin relatif et --relative-base définit la zone dans laquelle cette présentation est utilisée :

realpath -m -s --relative-to=/srv/www --relative-base=/srv -- /srv/www/assets /srv/logs /var/log

Vous obtenez assets, ../logs et /var/log. La base /srv contient ici l’origine /srv/www ; lorsque l’origine n’est pas sous la base, GNU utilise des chemins absolus. Les exemples officiels de realpath détaillent cette combinaison.

Résoudre un fichier de configuration dans un script

Enregistrez ce script complet sous chemin-config.sh et exécutez-le avec Bash. Il exige un argument non vide, résout un chemin existant, puis refuse un résultat qui n’est pas un fichier ordinaire. Il affiche le nom résolu, sans ouvrir ni exécuter le contenu de la configuration.

#!/usr/bin/env bash
if [ "$#" -ne 1 ] || [ -z "$1" ]; then
  printf '%s\n' 'Usage : bash chemin-config.sh fichier' >&2
  exit 64
fi

config=$(env QUOTING_STYLE=literal realpath -e -- "$1" && printf '.') || {
  statut=$?
  printf '%s\n' 'Le chemin ne peut pas être résolu.' >&2
  exit "$statut"
}
config=${config%.}
config=${config%$'\n'}
if [ ! -f "$config" ]; then
  printf '%s\n' 'Le chemin résolu ne désigne pas un fichier ordinaire.' >&2
  exit 66
fi
printf 'Configuration : %q\n' "$config"
bash chemin-config.sh "$espace/existant/config.ini"

L’affichage commence par Configuration et contient le chemin absolu du fichier de l’essai. Le statut 64 correspond à l’usage incorrect du script ; 66 signale ici un objet existant qui n’est pas un fichier ordinaire. Un échec de realpath conserve son propre statut.

La substitution de commande supprime normalement les sauts de ligne finaux. Le point ajouté après realpath permet de les conserver pendant la capture ; le script retire ensuite ce point et exactement le séparateur final de la commande. Les guillemets conservent les espaces et -- protège les noms commençant par un tiret.

Résoudre le nom puis tester -f ne garantit ni la permission de lecture ni l’immuabilité du fichier jusqu’à son utilisation. Ce script donne un exemple de diagnostic pour des fichiers choisis par l’utilisateur ; il n’est pas une primitive de confinement pour un service exposé à des entrées hostiles.

Traiter une liste avec des séparateurs NUL

La forme GNU -z termine chaque résultat par NUL, un octet nul qui ne fait pas partie des noms de fichiers Unix. Cette séparation évite de confondre un saut de ligne contenu dans un nom avec la fin d’un enregistrement. Le programme qui reçoit les résultats doit lui aussi conserver ces frontières.

Enregistrez le script suivant sous afficher-chemins.sh. Le format Bash %q rend les caractères particuliers visibles pour le lecteur ; les données venant de realpath sont consommées par la boucle comme des enregistrements NUL.

#!/usr/bin/env bash
if [ "$#" -eq 0 ]; then
  printf '%s\n' 'Usage : bash afficher-chemins.sh fichier [fichier...]' >&2
  exit 64
fi

set -o pipefail
env QUOTING_STYLE=literal realpath -e -z -- "$@" |
  while IFS= read -r -d '' chemin; do
    printf 'Chemin : %q\n' "$chemin"
  done
bash afficher-chemins.sh "$espace/existant/config.ini" "$espace/existant"

Vous devez obtenir deux chemins. Le script utilise -e et refuse donc une cible manquante. Avec plusieurs arguments, realpath peut avoir déjà produit des résultats valides avant de rencontrer une erreur : une sortie partielle ne constitue pas une liste entièrement validée. pipefail rend visible l’échec de realpath même si la boucle a pu lire certains résultats.

GNU Coreutils 9.12 documente aussi des citations de type shell lorsque la sortie est un terminal. env QUOTING_STYLE=literal exprime ici le choix de données littérales, indépendamment de cette présentation. Le manuel GNU de realpath précise le format et le statut de sortie. N’utilisez pas une variable Bash pour conserver des octets NUL.

Distinguer résolution, extraction et contrôle d’accès

Besoin Outil ou démarche Point de vigilance
Retirer le dernier composant d’une chaîne dirname Pas de résolution du système de fichiers
Lire le texte stocké dans un lien readlink sans mode de canonicalisation Un résultat relatif se rapporte au dossier du lien
Résoudre ou relativiser un chemin realpath Choisir existence, liens et base selon le besoin
Autoriser l’accès à un fichier Politique d’accès et ouverture adaptée par l’application Un nom calculé ne remplace pas le contrôle réel

Le guide de dirname explique l’extraction du parent sans recherche sur le disque. GNU préfère realpath pour la canonicalisation générale, même si readlink -f recouvre une partie de ces fonctions ; ne transposez pas automatiquement les options de l’un à l’autre sur tous les Unix.

Une comparaison de préfixe comme « commence par /srv/www » accepte aussi /srv/www-externe si elle ignore la frontière du composant. Même une comparaison correcte sur le chemin résolu ne supprime pas la possibilité de changements entre la vérification et l’ouverture : des liens et des répertoires peuvent être remplacés. Une application qui doit imposer une vraie limite d’accès doit appliquer cette limite lors de l’opération sur le fichier, avec les mécanismes du système adaptés à son environnement.