Romper todos los enlaces dentro de una carpeta

10

Tengo una carpeta que contiene una cierta cantidad de archivos que tienen enlaces duros (en la misma carpeta o en otro lugar), y quiero des-enlazar estos archivos, para que se vuelvan independientes, y los cambios en su contenido no afectarán otro archivo (su recuento de enlaces se convierte en 1).

A continuación, doy una solución que básicamente copia cada enlace duro a otra ubicación, luego lo vuelvo a colocar en su lugar.

Sin embargo, este método parece bastante tosco y propenso a errores, por lo que me gustaría saber si hay algún comando que desvincule un archivo para mí.

Respuesta cruda:

Busque archivos que tengan enlaces duros ( Editar : para buscar también sockets, etc. que tengan enlaces duros , use find -not -type d -links +1):

find      -type f -links +1 # files only
find -not -type d -links +1 # files, sockets etc.

Un método burdo para desenlazar un archivo (copiarlo a otra ubicación y moverlo hacia atrás): Editar: Como dijo Celada, es mejor hacer un cp -p a continuación, para evitar perder marcas de tiempo y permisos. Editar: cree un directorio temporal y cópielo en un archivo debajo de él, en lugar de sobrescribir un archivo temporal, minimiza el riesgo de sobrescribir algunos datos, aunque el mvcomando sigue siendo riesgoso (gracias @Tobu). Editar: intente crear el directorio temporal en el mismo sistema de archivos (@MikkoRantalainen).

# This is unhardlink.sh
set -e
for i in "$@"; do
  temp="$(mktemp -d -- "${i%/*}/hardlnk-XXXXXXXX")"
  [ -e "$temp" ] && cp -ip "$i" "$temp/tempcopy" && mv "$temp/tempcopy" "$i" && rmdir "$temp"
done

Entonces, para desvincular todos los enlaces duros ( Editar : cambiado -type fa -not -type d, ver arriba):

find -not -type d -links +1 -print0 | xargs -0 unhardlink.sh
Suzanne Dupéron
fuente
No consideraría eso 'crudo'. La única forma de hacerlo más rápido es probablemente hacer algún truco con la llamada al sistema sendfile () y desvincular el archivo de código abierto y reescribir el destino en el lugar. Francamente, no vale la pena el esfuerzo.
Matthew Ife
Por 'crudo', quiero decir que, por ejemplo, cuando ejecuté este comando usando el cp -iinterruptor, me escupió algunos mensajes preguntándome si debería anular ./fileXXXXXX(el $temparchivo), aunque tmpfile debería dar nombres de archivo únicos, por lo que debe ser algún tipo de condición de carrera o lo que sea, y con el riesgo de perder algunos datos.
Suzanne Dupéron
1
Es normal que el archivo exista, simplemente lo creó con tempfile (nb: en desuso a favor de mktemp, pero eso no es lo que causó su problema).
Tobu
1
Su unhardlink.shdebe crear el directorio temporal en el mismo directorio que contiene el archivo que necesita ser unhardlinked. De lo contrario, su llamada recursiva puede repetirse dentro de otro sistema de archivos y terminará moviendo cosas sobre los límites del sistema de archivos porque su directorio temporal está en el directorio de trabajo actual. Supongo que podrías pasar "$(dirname "$i")/hardlink-XXXXXX"como argumento a mktemp en su lugar.
Mikko Rantalainen
1
@MikkoRantalainen Muchas gracias, actualizado! Tenga en cuenta que si el sistema de archivos es una especie de unionfs o un fusesistema de archivos, en realidad podría enviarse path/to/hardlink-XXXa un medio de almacenamiento físico diferente path/to/original-file, pero no hay mucho que se pueda hacer al respecto.
Suzanne Dupéron

Respuestas:

9

Hay margen de mejora en su secuencia de comandos, por ejemplo, agregando una -popción al cpcomando para que los permisos y las marcas de tiempo se conserven en toda la operación de desvinculación, y podría agregar algo de manejo de errores para que el archivo temporal se elimine en caso de error, pero la idea básica de su solución es la única que funcionará. Para desvincular un archivo, debe copiarlo y luego mover la copia nuevamente sobre el nombre original. No existe una solución "menos cruda", y esta solución tiene condiciones de carrera en caso de que otro proceso acceda al archivo al mismo tiempo.

Celada
fuente
De hecho, siempre uso cp -a cuando copio cosas, para preservar todo, recurse y copio enlaces simbólicos como enlaces simbólicos. No sé por qué lo olvidé esta vez, pero después de ver su respuesta, entendí que había estropeado todas mis marcas de tiempo y que tenía que recuperarlas (bastante dolorosamente) de una copia de seguridad.
Suzanne Dupéron
5

Si desea quemar espacio en el disco y tiene una versión relativamente moderna de tar(por ejemplo, qué hay en Ubuntu 10.04 y CentOS 6), puede jugar con la --hard-dereferenceopción.

Algo como:

$ cd /path/to/directory
$ ls -l *
bar:
total 12
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 1
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 3

foo:
total 12
-rw-rw-r-- 2 cjc cjc 3 May  6 19:07 1
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 4

(donde había corrido ln foo/[12] bar)

$ tar cvf /tmp/dereferencing.tar --hard-dereference .
$ tar xvf /tmp/dereferencing.tar
$ ls -l *
bar:
total 12
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 1
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 3

foo:
total 12
-rw-rw-r-- 1 cjc cjc 3 May  6 19:07 1
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 4

Desde la página del manual:

   --hard-dereference
          follow hard links; archive and dump the files they refer to
cjc
fuente
Sospecho que hay poco alquitrán que no puede hacer. Buena solución
Joseph Kern
Olvidé mencionar que no tenía suficiente espacio en disco para copiar todo. Básicamente, su método es el mismo que cp -a --no-preserve=links /path/to/folder /path/to/copy && rm -rf /path/to/folder && mv /path/to/copy /path/to/folder, si no me equivoco. Sin embargo, supongo que su método sería más eficiente, porque el alquitrán implicaría menos búsquedas de disco, por lo que menos agitación. Se podría lograr lo mismo con rsync, con un rendimiento aún menor que el método cp :).
Suzanne Dupéron
1
Para evitar usar mucho disco extra, es posible ejecutar algo como, tar cvf - --hard-dereference . | tar xf -pero puede haber una condición de carrera que hará que las cosas exploten. No lo he intentado, y no estoy dispuesto a hacerlo en este momento.
cjc