¿Cómo puedo "volver a vincular" muchos enlaces simbólicos rotos?

54

Tengo un árbol de directorios que tiene un montón de enlaces simbólicos a los archivos bajo /home... sin embargo, me he mudado /homea /mnt/homey necesito una manera de "Volver a vincular" todos los enlaces simbólicos. ¿Existe tal funcionalidad o necesito escribir un script para hacerlo?

Como ejemplo, tengo algo como lo siguiente:

[root@trees ~]# ls -l /mnt/home/someone/something
total 4264
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 a -> /home/someone/someotherthing/a
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 b -> /home/someone/someotherthing/b
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 c -> /home/someone/someotherthing/c
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 d -> /home/someone/someotherthing/d
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 e -> /home/someone/someotherthing/e

/mnt/home/someone/something/subdir:
total 4264
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 a -> /home/someone/someotherthing/subdir/a
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 b -> /home/someone/someotherthing/subdir/b
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 c -> /home/someone/someotherthing/subdir/c
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 d -> /home/someone/someotherthing/subdir/d
lrwxrwxrwx 1 jnet www-data      55 2011-08-07 13:50 e -> /home/someone/someotherthing/subdir/e

Quiero un comando que encuentre todos los enlaces simbólicos y vuelva a vincular a los mismos lugares, pero debajo en /mnt/homelugar de/home

¿Existe tal comando?

Josh
fuente

Respuestas:

55

No hay un comando para retarget un enlace simbólico, todo lo que puede hacer es eliminarlo y crear otro. Suponiendo que tiene utilidades GNU (por ejemplo, bajo Linux o Cygwin no integrados), puede usar el -lnameprimario de findpara hacer coincidir los enlaces simbólicos por su objetivo y readlinkleer el contenido del enlace. No probado:

find /mnt/home/someone/something -lname '/home/someone/*' \
     -exec sh -c 'ln -snf "/mnt$(readlink "$0")" "$0"' {} \;

Sería mejor hacer que estos enlaces simbólicos sean relativos. Hay una pequeña utilidad conveniente llamada symlinks(originalmente por Mark Lords, ahora mantenida por J. Brandt Buckley), presente en muchas distribuciones de Linux. Antes del movimiento, o después de que haya restaurado los enlaces válidos como se indicó anteriormente, ejecute symlinks -c /mnt/home/someone/somethingpara convertir todos los enlaces simbólicos absolutos en el directorio especificado a enlaces simbólicos relativos a menos que crucen un límite del sistema de archivos.

Gilles 'SO- deja de ser malvado'
fuente
Sin ofender, esta es una gran frase, pero la sustitución de cuerdas de Bash probablemente podría hacer algo de magia con el cambio de ruta y sería más fácil.
0xC0000022L
@STATUS_ACCESS_DENIED ¿Cómo es eso? La única operación de cadena es anteponer /mntuna ruta; no necesita una operación de cadena más elegante que la concatenación.
Gilles 'SO- deja de ser malvado'
@Gilles: lo siento, estaba pensando más en tu comentario con los caminos relativos. Para una "traducción" exacta de su ejemplo, por supuesto tiene razón.
0xC0000022L
13

Sé que esto no es exactamente lo que el autor está solicitando, pero parece que ya tienen su respuesta, así que estoy agregando esto para otros como yo que se topan con la pregunta.

Lo siguiente debería ayudar si se requiere una solución más flexible, como tener un montón de enlaces simbólicos rotos que pueden repararse reemplazando parte de los objetivos del enlace simbólico.

p.ej. Después de un cambio de nombre de usuario, para reemplazar el antiguo nombre de usuario con el nuevo nombre de usuario en el destino de muchos enlaces, después de que el movimiento ya se haya realizado. Cree un script llamado replace-simlinks que se muestra a continuación:

#!/bin/bash
link=$1
# grab the target of the old link
target=$(readlink -- "$1")

# replace the first occurrence of oldusername with newusername in the target string
target=${target/oldusername/newusername}

# Test the link creation
echo ln -s -- "$target" "$link"

# If the above echo shows the correct commands are being issued, then uncomment the following lines and run the command again
#rm $link
#ln -s "$target" "$link"

y llámalo con el siguiente comando:

find /home/newusername/ -lname '/home/oldusername/*' -exec ~/bin/replace-simlinks {} \;

Espero que esto ayude a alguien

editar: Gracias Gilles por el arranque en este script y el consejo sobre el uso del script de enlaces simbólicos para hacer que los enlaces sean relativos.

Gerry
fuente
1
Encuentro esta solución mejor porque usa un reemplazo de cadena, lo que ayuda en casos en los que tiene que cambiar el nombre de una carpeta en el medio de la ruta. La solución también es bastante fácil de modificar para realizar transformaciones más complejas si es necesario.
Gallaecio
Recomendaría citar los argumentos para la sustitución de la cadena, ya que eso debe hacerse para usar barras, por ejemplo, para la ruta en la pregunta del OP. target=${target/"/home"/"/mnt/home"}Muy útil, sin embargo. Gracias.
Walter Nissen
3

Cree /homecomo un enlace simbólico para /mnt/home, y todos los enlaces simbólicos existentes serán válidos nuevamente.

Keith Thompson
fuente
2
El montaje de enlaces a menudo tiende a ser menos frágil que los enlaces simbólicos en escenarios donde los programas conocen los enlaces simbólicos y actúan de manera diferente dependiendo del hecho ...
0xC0000022L