Reemplazar enlaces simbólicos con archivos

31

¿Hay alguna manera fácil de reemplazar todos los enlaces simbólicos con el archivo al que se vinculan?

Marty Trenouth
fuente
3
¿Que problema estas tratando de resolver?
Daniel Beck
3
Tengo un montón de enlaces simbólicos a otra unidad. Necesito mover todos los archivos de esa unidad a los que se hace referencia en otro lugar para poder eliminarlos.
Marty Trenouth

Respuestas:

14

Para algunas definiciones de "fácil":

#!/bin/sh
set -e
for link; do
    test -h "$link" || continue

    dir=$(dirname "$link")
    reltarget=$(readlink "$link")
    case $reltarget in
        /*) abstarget=$reltarget;;
        *)  abstarget=$dir/$reltarget;;
    esac

    rm -fv "$link"
    cp -afv "$abstarget" "$link" || {
        # on failure, restore the symlink
        rm -rfv "$link"
        ln -sfv "$reltarget" "$link"
    }
done

Ejecute este script con nombres de enlaces como argumentos, por ejemplo, a través de find . -type l -exec /path/tos/script {} +

Gravedad
fuente
2
Gracias por la edición, anon, pero el guión hace nombres de archivo de la manija con espacios muy bien, citando las variables en todos los lugares necesarios. Cambiar "$var"a "${var}"es un noop en sh / bash. Eliminé el bashism ( [[), el resto es compatible con POSIX sh.
Grawity
Un método aún más robusto para manejar fallas de enlaces simbólicos es cpun archivo temporal en el mismo directorio y luego mv -fese archivo temporal en el original. Esto debería evitar problemas como personas que se mantienen presionadas Ctrl-Co que el sistema de archivos se llene entre los comandos rmy cp(evitando así que incluso los nuevos lnfuncionen). Los nombres de archivo temporales son fáciles de detectar ls -apara una limpieza posterior si es necesario.
Sr. Fooz
Como nota al margen, en serio no puedo recordar por qué no solo utilicé en abstarget=$(readlink -f "$link")lugar del bloque de casos , sino que podría haber sido por razones de portabilidad.
Grawity
17

Puede ser más fácil usar tar para copiar los datos a un nuevo directorio.

-H      (c and r mode only) Symbolic links named on the command line will
        be followed; the target of the link will be archived, not the
        link itself.

Podrías usar algo como esto

tar -hcf - sourcedir | tar -xf - -C newdir

tar --help:
-H, --format=FORMAT        create archive of the given format
-h, --dereference          follow symlinks; archive and dump the files they point to
Steve
fuente
1
Me encanta tu solución, pero no entiendo la última parte de tu mensaje. Para mí deberías hacer:tar -hcf - sourcedir | tar -xf - -C newdir
Seb DA ROCHA
1
De acuerdo, esta respuesta debería actualizarse según el comentario.
astabada
1
cp -Les la solución más simple
plesiv
@plesiv verdadero y compatible con posix, pero no todas las cpimplementaciones copiarán directorios.
Wyatt8740
16

Si te entendí correctamente, la -Lbandera del cpcomando debería hacer exactamente lo que quieres.

Simplemente copie todos los enlaces simbólicos y los reemplazará con los archivos a los que apuntan.

RedX
fuente
44
Gracias, casi lo que necesitaba: tenía que hacer algo como: cp -L files tmp/ && rm files && cp tmp/files . si aclaras, probablemente ayudarás a más personas ...
sabio
5

"fácil" será una función tuya, muy probablemente.

Probablemente escribiría un script que usa la utilidad de línea de comando "find" para encontrar archivos vinculados simbólicamente, luego llama a rm y cp para eliminar y reemplazar el archivo. Probablemente harías bien en hacer que la acción llamada por find compruebe que queda suficiente espacio libre antes de mover también el enlace sym.

Otra solución puede ser montar el sistema de archivos en cuestión a través de algo que oculta los enlaces simbólicos (como samba), luego simplemente copie todo. Pero en muchos casos, algo así introduciría otros problemas.

Una respuesta más directa a su pregunta es probablemente "sí".

Editar: según la solicitud de información más específica. De acuerdo con la página de manual de find , este comando enumerará todos los archivos de enlace sym en una profundidad de 2 directorios, desde /:

find / -maxdepth 2 -type l -print

( esto se encontró aquí )

Para obtener find para ejecutar algo al encontrarlo:

find / -maxdepth 2 -type l -exec ./ReplaceSymLink.sh {} \;

Creo que llamará a un script que acabo de inventar y pasará el nombre de archivo que acaba de encontrar. Alternativamente, puede capturar la salida de búsqueda al archivo (use "find [blah]> symlinks.data", etc.) y luego pasar ese archivo a un script que haya escrito para manejar la copia sobre el original con gracia.

James T Snell
fuente
? Un poco genérico, ¿puedes explicarlo?
Linker3000
2
La sintaxis es-exec ./ReplaceSymLink.sh {} \;
grawity
¡Esto es para buscar los elementos y el que está marcado como respuesta se usa para reemplazar los enlaces!
Marty Trenouth
3

Este es el oneliner que he usado: suponga que todos los archivos locales son enlaces a otro archivo en "fuente". Puede refinar la selección de archivos con patrones o "buscar". Por favor, comprenda antes de usar.

para f en *; do cp --remove-destination source / $ f $ f; hecho

Espero que esto ayude

MastroGeppetto
fuente
¿qué tal usar readlink: para f en *; do cp ---- remove-destination "$ (readlink $ f)" "$ f"; hecho
Diego
Sí, es una buena idea, pero pierde el control de la operación. Un límite de mi solución es que no funciona para archivos con espacios en blanco en el medio, o para listas muy largas. Probablemente la solución final es juntar el 'find --exec' anterior con el 'eliminar destino'. Alguien quiere probar?
MastroGeppetto
los espacios en blanco en el medio deben mitigarse poniendo "$ f" en comillas dobles Así es como lo uso con "find & xargs" en lugar de "for loop": find ./ -type l -print0 | xargs -0 -n1 -i sh -c 'cp --remove-destination $ (readlink "{ } ")" {} "'Lo publiqué como una respuesta separada para la visibilidad. superuser.com/a/1329543/499386
Diego
2
find . -type l -exec cp --dereference --recursive '{}' '{}'.dereferenced \;

Hará una copia de cada archivo / carpeta con enlace simbólico <filename>.dereferenced, que es más seguro (si es menos conveniente) que simplemente reemplazarlos directamente. Mover los datos copiados a los nombres de archivo de los enlaces simbólicos se deja como ejercicio para el lector.

blahdiblah
fuente
2

Usando algunas de las ideas anteriores, el siguiente comando reemplazará recursivamente cada enlace simbólico con una copia del original:

find . -type l -exec bash -c "echo 'Replacing {} ...';  cp -LR '{}' '{}'.dereferenced;  rm '{}';  mv '{}'.dereferenced '{}'" \;
Antonio
fuente
1

Hay muchas buenas respuestas allí, pero desde que estabas buscando algo fácil

for i in *; do link=$(readlink $i) && rm $i && mv $link $i; done
xaocon
fuente
Una pequeña mejora ayudará a encontrar enlaces simbólicos: para lnk en `find. -tipo l`; do src = $ (readlink $ lnk) && rm $ lnk && mv $ src $ lnk; hecho (no probado con la jerarquía, sin embargo, tenga cuidado con los backticks)
Roman Susi
1

Tuve el mismo problema con la copia de enlaces de gwenview cuando normalmente esperarías que siguiera el enlace y copiara el archivo.

Lo que hice para 'limpiar' el directorio siguiendo todos los enlaces y copiando todos los archivos apuntados al directorio, fue crear un directorio temporal, ejecutar, por ejemplo

"for file in `ls`; do cp -L $file scratchdir/$file; done; mv scratchdir/* ./" 

Esto es demasiado peligroso para poner en un script ya que no hay verificación pero solucionó mi problema.

usuario225929
fuente
1
for f in *; do cp --remove-destination $(readlink "$f") "$f"; done
Lorem Ipsum Dolor
fuente
0

Hice una versión de una línea, ya que mi hotel web no permitía bash-scripts y funcionó bien para mí:

before:
> find . -type l | wc -l
358

> for link in `find . -type l` ; do echo "$link : "; ls -al $link ; cp $link notalink; unlink $link; mv notalink $link ; ls -al $link; done
...

after:
> find . -type l | wc -l
0
Autotrader
fuente