Das Symptom
Jemand steht im Projektordner und tippt ln -s config.yml /etc/dienst/config.yml, um die Datei dort zu verlinken. Der Link entsteht ohne Fehlermeldung, aber der Dienst findet seine Konfiguration nicht, und ls zeigt ihn rot oder blinkend an. Die Annahme dahinter: ln löse den relativen Namen beim Anlegen vom aktuellen Verzeichnis aus auf und speichere das Ergebnis. Tatsächlich speichert der Link nur den Text config.yml, und der wird erst beim Zugriff ausgewertet, relativ zum Verzeichnis, in dem der Link liegt, also /etc/dienst/. Dieselbe Falle schlägt zu, wenn ein funktionierender relativer Link später mit mv in ein anderes Verzeichnis geschoben wird.
Die „Layer 8“-Wahrheit
Die Handbuchseite ln(1) sagt es in einem Satz: „Symbolic links can hold arbitrary text; if later resolved, a relative link is interpreted in relation to its parent directory.“ Beliebiger Text, später aufgelöst, in Bezug auf das Elternverzeichnis des Links. Nichts davon hat mit dem Verzeichnis zu tun, in dem der Befehl ausgeführt wurde, und ln prüft beim Anlegen nicht einmal, ob das Ziel existiert.
symlink(7) ergänzt beides ausdrücklich. Zum Anlegen: „There is no requirement that the pathname referred to by a symbolic link should exist. A symbolic link that refers to a pathname that does not exist is said to be a dangling link.“ Und zum Verschieben: „if the symbolic link references a file via a relative path, moving it to another directory may very well cause it to stop working, since the path may no longer be correct.“ Der Link ist also kein Zeiger auf eine Datei, sondern ein gespeicherter Text, der bei jedem Zugriff neu gelesen und vom Ort des Links aus interpretiert wird.
Praktisch heißt das: Entweder ein absolutes Ziel angeben, oder den relativen Pfad vom Speicherort des Links aus denken, oder ln -sr verwenden, das laut ln(1) den Link relativ zum Ort des Links erzeugt („-r, –relative with -s, create links relative to link location“). Die dritte Variante ist die bequemste, weil man das Ziel so schreiben darf, wie man es vor sich sieht, und trotzdem einen Link bekommt, der dort stimmt, wo er liegt.
Der Irrtum ist verständlich, weil fast jeder andere Befehl relative Pfade sofort und vom Arbeitsverzeichnis aus auflöst. cp, mv und cat tun das, und man gewöhnt sich daran, dass ein relativer Name immer das meint, was man gerade vor sich hat. ln -s ist die Ausnahme: Es schreibt den Namen auf und überlässt die Auflösung dem, der den Link später öffnet, von dort aus, wo der Link dann liegt.