¿Romper un enlace duro en el lugar?

13

Mantengo mis dotfiles bajo control de versiones y el script que los implementa crea enlaces duros. También uso etckeeperpara poner mi /etcbajo control de versiones. Recientemente recibí advertencias como esta:

warning: hard-linked files could cause problems with bzr

Una copia simple ( cp filename.ext filename.ext) no funcionará:

cp: `filename.ext' and `filename.ext' are the same file

Cambiar el nombre / mover un archivo, excepto a través de volúmenes, tampoco rompe el enlace duro.

Entonces mi pregunta es: ¿hay alguna manera de romper un enlace duro a un archivo sin tener que saber dónde están los otros enlaces duros a ese archivo?

0xC0000022L
fuente
2
El comando "rm" rompe los enlaces duros.
Johan

Respuestas:

14
cp -p filename filename.tmp
mv -f filename.tmp filename

Haciéndolo programable:

dir=$(dirname -- "$filename")
tmp=$(TMPDIR=$dir mktemp)
cp -p -- "$filename" "$tmp"
mv -f -- "$tmp" "$filename"

Hacer la copia primero, luego moverla a su lugar, tiene la ventaja de que el archivo cambia atómicamente de ser un enlace rígido a ser una copia separada (no hay un momento en el que filenamesea ​​parcial o faltante).

Gilles 'SO- deja de ser malvado'
fuente
7

Probablemente quiere decir que quiere dividir el enlace duro en un archivo separado e independiente.

mv hardlink tempname && cp tempname hardlink && rm tempname

Un enlace duro es la conexión entre una entrada en el directorio y el bloque de inodo en el disco.

los inodes almacenan metadatos de archivos, y para archivos pequeños, algunos sistemas de archivos almacenan datos en el inodo, de lo contrario apuntan a los bloques de datos, y para archivos muy grandes listas indirectas y doble-indirectas de punteros a unidades de asignación de disco.

En cualquier caso, la conexión entre el nombre del archivo (que es lo que produce el comando ls) y el bloque de inodo que almacena estos metadatos, se denomina enlace duro.

Tener múltiples enlaces duros a un solo archivo significa el mismo inodo al que hace referencia más de una entrada de directorio, posiblemente en diferentes directorios (en un solo sistema de archivos)

rm elimina la entrada del nombre del archivo del directorio. Una vez que ningún archivo hace referencia a un inodo, su espacio se libera para que otros archivos lo utilicen.

Johan
fuente
En efecto. Esto es lo que el cpy mvejemplos implícito. Así que no puedo evitar usar un archivo temporal, ya veo.
0xC0000022L
@ 0xC0000022L, No, los inodes no contienen punteros a otros inodes. Solo para bloques de datos (o pueden duplicarse como espacio de datos si el objeto es pequeño).
vonbrand
44
Asegúrese de que los permisos y otros datos se conservan al copiar, es decir. uso cp -a(al menos GNU coreutils).
vonbrand
@ 0xC0000022L, si observa detenidamente, solo hay un nombre temporal para el archivo original, solo se crea un nuevo archivo en el último paso.
vonbrand
@vonbrand De hecho, pueden, dependiendo de qué sistema de archivos se esté utilizando.
Johan
4

Ponga esto al final de su archivo ~ / .bashrc.

delink () { tmpfile="$1$(date)"; cp -a "$1" "$tmpfile"; mv "$tmpfile" "$1"; }

Ejecútalo así

delink filename
Rucent88
fuente
3

La mejor manera de hacerlo con un script bash sería algo como esto:

if [ -f "$1" ] ; then
dir="$(dirname -- "$1")"
tmpfile="$(mktemp --tmpdir="$dir")"
cp --preserve=all -f -- "$1" "$tmpfile"
mv -f -- "$tmpfile" "$1"
fi

puntos a tener en cuenta:

  • compruebe si el archivo es un archivo normal antes de intentar copiarlo
  • mantenga el archivo antiguo en su lugar hasta que la copia esté lista
  • utilizar mktemppara generar un archivo que se garantiza que no existe
  • use -fpara forzar la sobrescritura y --preserve=allpara mantener los metadatos lo más similares posible al archivo original
  • use --y "para citar rutas que contienen espacios y / o que comienzan con-

Hacer el reemplazo sin crear un archivo temporal no es posible con las llamadas actuales del sistema Linux (3.16): si bien es posible sobrescribir un archivo atómicamente (es decir, eliminar el archivo antiguo y reemplazarlo por uno nuevo como una sola operación), no es posible hacerlo con un archivo que no tiene nombre en el sistema de archivos (es decir, un archivo temporal creado usando el O_TMPFILEindicador de openfunción) porque la renamefunción requiere un nombre de archivo como entrada (no hay una versión renameque tome como entrada un descriptor de archivo) ver aquí para más detalles)

pqnet
fuente
1
Tenga en cuenta que no pudo citar el nombre en su dirnamey mktempllamadas. Arreglado eso para ti ...
derobert
@derobert oh gracias, pero esto no funcionará ya que hay comillas dobles anidadas ... ¡necesito otra solución! Un poco de odio bash
pqnet
3
Funcionará debido a la $( ... )sustitución de comandos de estilo. Una razón es mejor que el ` ... `estilo.
derobert
@derobert agradable, no lo sabía. Además, ¿cómo puede usar `etiquetas de código en línea internas?
pqnet
Puedes escapar de ellos con barras invertidas. Entonces, para que `te pongan: `\``(por supuesto, me escapé de eso para que te muestre lo que escribes).
derobert
-1

El comando que estás buscando es unlink

Jenny D
fuente
Quizás mi pregunta no estaba clara, pero por "en el lugar" y los ejemplos con cpy mvquise aclarar que deseo que el archivo exista después.
0xC0000022L
Ah, no me pareció claro, no. Deberías ir con la respuesta de Johan, entonces.
Jenny D
1
El comando "desvincular" simplemente elimina el archivo (lo que hace la llamada al sistema unlink ()).
Raúl Salinas-Monteagudo
-1

Si está buscando todos los nombres de archivo que están vinculados a este archivo, puede usar:

find -samefile myknowhardlinkfile

también ls -il myknowhardlinkfilele mostrará el número de nombre de archivo vinculado al mismo inodo (tercer campo).

101612442 -rw-rw-r--. 2 me me 0 Aug  5 07:07 myknowhardlinkfile
Jean-Pierre Laurin
fuente
Esto en realidad no responde la pregunta, aunque podría ser útil.
Flimm