Linux / Commande readlink : lire la cible d’un lien symbolique

readlink affiche le texte enregistré dans un lien symbolique, sans lire le contenu du fichier cible. Une cible relative se rapporte au répertoire du lien ; pour obtenir un chemin absolu résolu, GNU propose aussi des modes de canonicalisation, mais recommande généralement realpath pour ce besoin.

La distinction est essentielle pour diagnostiquer un déploiement, un raccourci de configuration ou un lien cassé : « quel texte contient ce lien ? » et « vers quel chemin se résout cette entrée ? » sont deux questions différentes.

Lire une cible de lien, pas le contenu d’un fichier

readlink -- courant

Si courant est un lien qui contient releases/v1, le résultat ordinaire est releases/v1 suivi d’un saut de ligne. Cela ne signifie pas que le fichier ou dossier releases/v1 a été ouvert ou que la cible existe. Les composants nécessaires pour atteindre le lien lui-même doivent cependant être accessibles.

Si le dernier composant est un fichier ordinaire ou un répertoire ordinaire, le mode de lecture du lien ne produit pas de cible et échoue. En revanche, un chemin traversant un lien dans un répertoire intermédiaire peut tout de même conduire au lien final dont le texte est demandé.

if readlink -v -- /etc/passwd; then
  printf '%s\n' 'Cette entrée finale est un lien lisible.'
else
  printf '%s\n' 'Aucune cible de lien n’a pu être lue.' >&2
fi

Cette commande teste ce qui se trouve réellement sur la machine : ne supposez pas que /etc/passwd est toujours un fichier ordinaire. GNU -v demande un diagnostic ; vérifiez le statut au lieu de déduire la réussite de l’absence de message.

Créer un exemple de liens relatifs, absolus et cassés

Sur Linux, les commandes suivantes créent un nouvel espace temporaire, une petite configuration et quatre véritables liens symboliques. Elles ne remplacent aucun lien existant. Si ln -s échoue, arrêtez l’essai : une copie de dossier ne reproduit pas le comportement d’un lien.

espace=$(mktemp -d) || exit 1
mkdir -p -- "$espace/app" "$espace/releases/v1" || exit 1
printf 'version=1\n' > "$espace/releases/v1/config.ini"
ln -s -- ../releases/v1 "$espace/app/courant" || exit 1
ln -s -- "$espace/releases/v1" "$espace/app/absolu" || exit 1
ln -s -- ../releases/manquante "$espace/app/casse" || exit 1
ln -s -- courant "$espace/app/second" || exit 1
printf 'Espace de travail : %s\n' "$espace"

readlink -- "$espace/app/courant"
readlink -- "$espace/app/absolu"
readlink -- "$espace/app/casse"
readlink -- "$espace/app/second"
Lien sous app Texte affiché Interprétation
courant ../releases/v1 Cible relative à app
absolu Chemin de l’espace temporaire suivi de /releases/v1 Cible absolue
casse ../releases/manquante Texte lisible même si la cible manque
second courant Lecture d’un seul lien, sans dérouler toute la chaîne

Le lien courant se trouve dans app : son ../releases/v1 conduit au dossier releases/v1 de l’espace temporaire. Le même texte dans un lien situé ailleurs peut désigner une autre cible. Changer le répertoire de votre terminal ne change pas cette base d’interprétation.

Un lien relatif peut rester utilisable lorsqu’on déplace ensemble le lien et sa cible en conservant leur relation. Un lien absolu continue de viser son chemin absolu ; le déplacement du seul lien ou de toute une arborescence peut donc avoir des conséquences différentes. Choisissez le type de lien selon la structure réellement attendue.

Examiner un lien cassé sans confondre les tests

readlink -- "$espace/app/casse"
if [ -L "$espace/app/casse" ] && [ ! -e "$espace/app/casse" ]; then
  printf '%s\n' 'Le lien existe, mais sa cible ne peut pas être constatée comme existante.'
fi

Le test Bash -L, synonyme de -h, examine si l’entrée finale est un lien symbolique. -e suit le lien pour tester l’existence du résultat. Dans cet espace maîtrisé, le message correspond à la cible volontairement absente.

Sur un système réel, ce couple de tests ne prouve pas à lui seul que le nom cible a été supprimé : un accès refusé, une chaîne de liens en boucle ou un changement concurrent peuvent empêcher le test d’atteindre la cible. Lisez le texte du lien, examinez les composants et les permissions nécessaires, puis résolvez le chemin avec la condition d’existence voulue. Ne supprimez pas un lien uniquement parce que -e est faux.

Choisir entre -f, -e et -m

Ces options GNU passent dans un autre mode : readlink calcule alors un chemin canonique absolu et peut accepter un fichier ordinaire. Les liens rencontrés sont résolus et les composants de ponctuation sont traités.

Option GNU Condition d’existence Résultat utile
-f, --canonicalize Les composants sauf le dernier doivent exister et être disponibles ; le séparateur final est ignoré. Nom final éventuellement absent sous un parent existant
-e, --canonicalize-existing Tous les composants doivent exister ; un séparateur final exige un répertoire. Chemin existant au moment de la résolution
-m, --canonicalize-missing Les composants manquants ou indisponibles sont traités comme des répertoires. Calcul d’un chemin projeté sans preuve d’existence
readlink -f -- "$espace/app/courant/config.ini"
readlink -e -- "$espace/app/courant/config.ini"
readlink -f -- "$espace/app/casse"

Les deux premières lignes pointent vers releases/v1/config.ini de l’espace temporaire. La troisième peut réussir avec un résultat finissant par /releases/manquante, puisque le parent releases existe et que seul le dernier composant résolu manque.

readlink -e -- "$espace/app/casse"
readlink -f -- "$espace/app/casse/config.ini"

Ces deux appels échouent dans l’exemple. Le premier exige la cible absente. Pour le second, manquante devient un composant intermédiaire du chemin résolu : -f ne permet pas à ce parent de manquer. Exécutez ces appels séparément si votre shell s’arrête sur les erreurs.

readlink -m -- "$espace/futur/../archives/nouveau.txt"

Le résultat finit par /archives/nouveau.txt sans créer ce chemin. Pour le choix des modes et les chemins relatifs à une base, utilisez plutôt realpath et ses options de canonicalisation. Une chaîne calculée par -m ne prouve pas qu’un fichier pourra être créé à cet emplacement.

Conserver l’association entre un lien et sa cible

GNU accepte plusieurs opérandes, mais une cible peut manquer dans la sortie si la lecture d’un des liens échoue. Ne supposez donc pas que la troisième ligne produite correspond toujours au troisième argument. Pour un rapport lisible, inspectez chaque lien séparément et affichez son nom avec son résultat.

Enregistrez le script complet suivant sous inspecter-liens.sh et lancez-le avec Bash et GNU readlink. Il ne crée, ne modifie et ne supprime aucun lien. Il poursuit l’examen après une erreur, puis renvoie 1 si au moins une lecture a échoué ; 64 signale l’absence d’arguments.

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

resultat=0
for lien in "$@"; do
  cible=$(env QUOTING_STYLE=literal readlink -v -- "$lien" && printf '.') || {
    printf 'Lecture impossible : %q\n' "$lien" >&2
    resultat=1
    continue
  }
  cible=${cible%.}
  cible=${cible%$'\n'}
  case "$cible" in
    /*) nature='absolue' ;;
    *) nature='relative' ;;
  esac
  printf '%q -> %q (%s)\n' "$lien" "$cible" "$nature"
done
exit "$resultat"
bash inspecter-liens.sh "$espace/app/courant" "$espace/app/absolu" "$espace/app/casse" "$espace/app/second"

Le rapport classe courant, casse et second comme des cibles relatives, et absolu comme une cible absolue. « Relative » ou « absolue » décrit le texte stocké, pas sa validité ni la permission d’accès. Le lien casse doit être lu avec succès dans cet exemple, puisque la lecture ne requiert pas l’existence de sa cible.

Le point ajouté après readlink empêche la substitution de commande de supprimer les éventuels sauts de ligne finaux de la cible. Le script retire ensuite le point et exactement le séparateur ajouté par readlink. Le format Bash %q rend visibles les caractères particuliers et protège la lisibilité du rapport, sans transformer son texte en code à exécuter.

Les guillemets autour des arguments et -- évitent le découpage des espaces, l’expansion de motifs et l’interprétation d’un nom comme option. Si une lecture échoue, le script affiche le lien concerné et passe au suivant : il ne réutilise pas une ancienne cible comme si elle appartenait au nouvel argument.

Distinguer -n, -z et la présentation à l’écran

-n supprime le séparateur de sortie pour un seul opérande. Il ne résout pas le problème d’un caractère spécial à l’intérieur d’une cible. GNU avertit si -n est utilisé avec plusieurs opérandes ; pour une liste, choisissez plutôt les séparateurs NUL de -z.

readlink -n -- "$espace/app/courant"
printf '\n'

Le printf suivant ajoute ici volontairement un saut de ligne pour retrouver un terminal lisible. Pour un traitement par programme, le saut de ligne n’est pas un séparateur général sûr : un lien peut contenir un nom qui en possède déjà un.

set -o pipefail
env QUOTING_STYLE=literal readlink -z -- "$espace/app/courant" "$espace/app/casse" |
  while IFS= read -r -d '' cible; do
    printf 'Cible : %q\n' "$cible"
  done

Cette boucle Bash lit deux enregistrements terminés par NUL. Elle affiche les cibles, sans y associer les noms des liens ; utilisez le rapport par lien précédent si cette association est nécessaire. pipefail rend l’erreur du producteur visible même après la lecture de résultats partiels. Les variables Bash ne peuvent pas conserver les octets NUL eux-mêmes.

Le manuel GNU Coreutils 9.12 prévoit des citations de type shell quand la sortie est un terminal. QUOTING_STYLE=literal explicite le choix de données littérales dans les exemples de traitement. Ce comportement de présentation peut différer d’une version ancienne ; les guillemets éventuellement affichés ne sont pas forcément enregistrés dans le lien.

Comprendre les diagnostics et les différences entre commandes

Sur GNU, readlink -s et readlink -q masquent la plupart des diagnostics. À l’inverse, readlink -v les demande. Le manuel GNU Coreutils 9.12 indique que le défaut dépend de POSIXLY_CORRECT : silencieux si la variable est absente, détaillé si elle est définie. Des versions anciennes restent silencieuses par défaut dans les deux cas ; utilisez -v explicitement pour demander les diagnostics. Aucune de ces options ne convertit un échec en réussite.

Attention au même nom d’option : realpath -s signifie ne pas développer les liens, tandis que readlink -s signifie rester silencieux sur les erreurs. Vérifiez le manuel GNU de readlink avant de transposer une ligne de commande.

La spécification POSIX de readlink définit la lecture d’un lien et l’option -n ; les modes GNU -f/-e/-m, -z et les listes d’opérandes ne doivent pas être supposés identiques sur tous les Unix. Consultez l’aide de l’implémentation réellement utilisée.

Pour un examen humain, ls -l montre le lien dans une liste et stat examine ses métadonnées ou celles de sa cible selon le mode. Évitez de découper la sortie de ls sur la flèche pour automatiser la lecture du lien : les noms peuvent eux-mêmes contenir cette ponctuation. readlink fournit le texte directement ; realpath sert à résoudre le chemin.

Enfin, une cible lisible ou un chemin résolu n’est pas un accès verrouillé à l’objet. Le lien et ses répertoires peuvent changer entre l’observation et l’ouverture. Conservez les permissions et les garanties de l’opération réelle comme critères distincts du diagnostic de chemin.