Un enlace simbólico (symlink) es un tipo especial de archivo cuyo contenido es una cadena de texto: una ruta. Cuando el kernel resuelve un path y encuentra un symlink, sustituye ese segmento por la ruta almacenada dentro del enlace y continúa la resolución. Es literalmente una indirección a nivel de VFS (Virtual File System): el inodo del symlink no tiene nada que ver con el inodo del archivo destino. Eso lo diferencia fundamentalmente de un hard link, que sí comparte inodo.
La consecuencia directa de que un symlink almacene una ruta —no una referencia a inodo— es que puede cruzar filesystems sin problema, y puede apuntar a directorios. También significa que si el destino desaparece, el symlink sigue existiendo perfectamente; simplemente apunta a nada. Eso es un dangling symlink (enlace colgante), y es la fuente más habitual de confusión con esta herramienta.
Los permisos que ves en un symlink (lrwxrwxrwx) son irrelevantes para el control de acceso. El kernel los ignora por completo y evalúa los permisos del archivo o directorio destino en el momento de resolver el enlace. Los rwxrwxrwx están ahí por razones históricas y de portabilidad POSIX; no los cambies, no significan nada.
¿Cuándo usas symlinks en lugar de copias o hard links? Cuando necesitas que un path fijo apunte a algo que puede cambiar de versión o ubicación, o cuando quieres unificar acceso desde distintos lugares del árbol sin duplicar datos. Un symlink roto en producción —porque alguien borró el destino sin actualizar el enlace— puede tumbar un servicio tan silenciosamente como un error de configuración. ls -l mostrará el enlace en rojo si el terminal lo soporta, pero los logs del servicio dirán simplemente “No such file or directory”.
# Preparamos un directorio de trabajo para el ejemplo
mkdir -p /tmp/demo_symlinks/proyecto/{v1,v2}
# Creamos archivos de contenido en cada versión
echo "config de la versión 1" > /tmp/demo_symlinks/proyecto/v1/config.conf
echo "config de la versión 2" > /tmp/demo_symlinks/proyecto/v2/config.conf
# Creamos un symlink 'actual' que apunta a v1
# ln -s ORIGEN DESTINO — el orden importa, igual que cp
ln -s /tmp/demo_symlinks/proyecto/v1 /tmp/demo_symlinks/proyecto/actual
# ls -l muestra la 'l' en el primer campo y la flecha con el destino
ls -l /tmp/demo_symlinks/proyecto/
# Leemos a través del symlink — el kernel resuelve la indirección
cat /tmp/demo_symlinks/proyecto/actual/config.conf
# readlink muestra exactamente qué string contiene el symlink, sin resolver
readlink /tmp/demo_symlinks/proyecto/actual
# Cambiamos a v2 de forma atómica: ln -sfn reemplaza el symlink existente
# -f: fuerza si ya existe -n: trata el destino como archivo, no como directorio
ln -sfn /tmp/demo_symlinks/proyecto/v2 /tmp/demo_symlinks/proyecto/actual
# Verificamos que ahora apunta a v2
cat /tmp/demo_symlinks/proyecto/actual/config.conf
# ── Symlink relativo vs absoluto ──────────────────────────────────────
# Un symlink relativo se resuelve desde el directorio donde VIVE el enlace,
# no desde el directorio de trabajo actual del proceso
cd /tmp/demo_symlinks/proyecto
ln -s v2/config.conf enlace_relativo.conf
# readlink -f resuelve toda la cadena (incluidos symlinks intermedios)
# y devuelve el path canónico absoluto del archivo final
readlink -f enlace_relativo.conf
# realpath hace lo mismo, pero además acepta --relative-to para comparaciones
realpath enlace_relativo.conf
# ── Dangling symlink ──────────────────────────────────────────────────
ln -s /tmp/demo_symlinks/proyecto/v3/config.conf /tmp/demo_symlinks/dangling.conf
# El symlink existe pero v3 no existe: intento de acceso falla con ENOENT
ls -l /tmp/demo_symlinks/dangling.conf # aparece el enlace (en rojo en muchos terminales)
cat /tmp/demo_symlinks/dangling.conf # → No such file or directory
# Para encontrar dangling symlinks en un árbol completo:
# -xtype l coincide con symlinks cuyo destino NO existe (a diferencia de -type l)
find /tmp/demo_symlinks -xtype l
# ── Ejemplos reales del sistema ───────────────────────────────────────
# /bin es un symlink a usr/bin desde la fusión de directorios en Debian
ls -la /bin
# update-alternatives gestiona symlinks en /etc/alternatives
# Este es el mecanismo detrás de 'python3' o 'editor' en el sistema
ls -la /etc/alternatives/python3 2>/dev/null || \
ls -la /etc/alternatives/editor
# Resolución completa de la cadena: editor → /etc/alternatives/editor → nano (o lo que sea)
readlink -f /usr/bin/editor
# Limpieza
cd /
rm -rf /tmp/demo_symlinks
Qué está pasando en cada decisión
El orden de ln -s es origen-destino, igual que cp. El error más frecuente es invertirlos. Si lo inviertes y ambos paths existen, creas un enlace dentro del directorio que creías que era el destino —Linux no se queja, y el resultado es desconcertante.
ln -sfn para el reemplazo atómico merece atención. La opción -f borra el symlink existente antes de crear el nuevo. La opción -n es crítica cuando el destino es un directorio que ya existe: sin -n, ln -sf intentaría crear el nuevo symlink dentro del directorio destino en lugar de reemplazarlo. Con -n, trata el destino como si fuera un archivo ordinario a efectos de reemplazo. Esta combinación es el patrón estándar para hacer deploys con current -> vX sin downtime.
Symlink relativo vs. absoluto: el enlace enlace_relativo.conf que apunta a v2/config.conf se resuelve desde /tmp/demo_symlinks/proyecto/ porque ahí vive el enlace, no desde donde ejecutes el comando. Si mueves el directorio proyecto a otro lugar, un symlink relativo sigue funcionando; uno absoluto se rompe. Para symlinks que viajan dentro de paquetes o repositorios, relativo es la opción correcta. Para configuración de sistema donde la ubicación es fija, absoluto es más legible y menos ambiguo.
readlink sin flags devuelve exactamente el string guardado dentro del inodo del symlink. No resuelve nada. Es lo que necesitas cuando quieres saber adónde apunta el enlace en sí, no adónde lleva la cadena completa. readlink -f y realpath hacen lo mismo: resuelven cada componente del path recursivamente hasta llegar al inodo final real, y fallan si algún componente intermedio no existe (a diferencia de readlink solo, que simplemente imprime el string aunque el destino no exista).
find -xtype l para dangling symlinks es el truco que importa. -type l encuentra todos los symlinks, existan o no sus destinos. -xtype l aplica el test al archivo después de resolver el enlace: si el enlace está roto, el destino no existe y no tiene tipo real, así que la condición se cumple para los colgantes. Es la forma idiomática de auditarlos en un árbol.
La cadena /usr/bin/editor → /etc/alternatives/editor → /usr/bin/nano ilustra por qué readlink -f existe. update-alternatives construye cadenas de dos niveles: el nombre genérico apunta a /etc/alternatives/, que a su vez apunta al binario real de la implementación elegida. Si corres solo readlink /usr/bin/editor ves el primer salto; necesitas -f para llegar al ejecutable efectivo que va a correr cuando escribas editor en la terminal.
N° 28