¿Cómo puedes ver el enlace real de ls?


97

Corro

ln /a/A /b/B

Me gustaría ver en la carpeta adonde están los puntos a presentar una por ls.


1
Los enlaces duros no son punteros, los enlaces simbólicos sí. Son varios nombres para el mismo archivo (inodo). Después de una link(2)llamada al sistema, no tiene sentido cuál es el original y el enlace. Por eso, como señalan las respuestas, la única forma de encontrar todos los enlaces es find / -samefile /a/A. Porque una entrada de directorio para un inodo no "conoce" otras entradas de directorio para el mismo inodo. Todo lo que hacen es volver a contar el inodo para que se pueda eliminar cuando el apellido es unlink(2)ed. (Este es el "conteo de enlaces" en la lssalida).
Peter Cordes

@PeterCordes: ¿La cuenta real se almacena realmente en la entrada de enlace duro? Eso es lo que implica su redacción ("Todo lo que hacen es volver a contar el inodo ...") Pero eso no tendría sentido si los enlaces no saben nada el uno del otro, ya que cuando se actualiza, todos los demás tendrían que estar actualizado. ¿O el recuento se almacena en el inodo mismo? (Perdóname si es una pregunta tonta, me considero un novato y todavía estoy aprendiendo).
bote solitario

1
El recuento se almacena en el inodo, como finalmente descubrió que debe ser el caso, a partir de los otros hechos. :) Las entradas del directorio se denominan punteros a inodes. Lo llamamos "enlace duro" cuando tiene varios nombres apuntando al mismo inodo.
Peter Cordes

Respuestas:


171

Puede encontrar el número de inodo para su archivo con

ls -i

y

ls -l

muestra el recuento de referencias (número de enlaces duros a un inodo particular)

Después de encontrar el número de inodo, puede buscar todos los archivos con el mismo inodo:

find . -inum NUM

mostrará los nombres de archivo para inode NUM en el directorio actual (.)


46
podrías correr a buscar. -samefile filename
BeowulfNode42

1
@ BeowulfNode42 Este comando es excelente, pero al menos necesita la carpeta raíz compartida de los mismos archivos.
Itachi

1
esta respuesta da un "hacer esto" pragmático, pero creo firmemente que @LaurenceGonsalves responde las preguntas "cómo" y / o "por qué".
Trevor Boyd Smith el

65

Realmente no hay una respuesta bien definida a su pregunta. A diferencia de los enlaces simbólicos, los enlaces duros no se pueden distinguir del "archivo original".

Las entradas de directorio consisten en un nombre de archivo y un puntero a un inodo. El inodo a su vez contiene los metadatos del archivo y (apunta a) el contenido real del archivo). Crear un enlace duro crea otro nombre de archivo + referencia al mismo inodo. Estas referencias son unidireccionales (al menos en sistemas de archivos típicos): el inodo solo mantiene un recuento de referencias. No hay una forma intrínseca de averiguar cuál es el nombre de archivo "original".

Por cierto, esta es la razón por la cual se llama al sistema para "eliminar" un archivo unlink. Simplemente elimina un enlace duro. El inodo y los datos adjuntos se eliminan solo si el recuento de referencia del inodo cae a 0.

La única forma de encontrar las otras referencias a un inodo dado es buscar exhaustivamente en el sistema de archivos comprobando qué archivos se refieren al inodo en cuestión. Puede usar 'prueba A -ef B' desde el shell para realizar esta verificación.


35
Eso significa que no existe un enlace rígido a otro archivo , ya que el archivo original también es un enlace rígido; los enlaces duros apuntan a una ubicación en el disco .
jtbandes

12
@jtbandes: los enlaces duros apuntan a un inodo que apunta a los datos reales.
dash17291

33

UNIX tiene enlaces duros y enlaces simbólicos (hechos con "ln"y "ln -s"respectivamente). Los enlaces simbólicos son simplemente un archivo que contiene la ruta real a otro archivo y puede cruzar sistemas de archivos.

Los enlaces duros han existido desde los primeros días de UNIX (que puedo recordar de todos modos, y eso se remonta bastante tiempo). Son dos entradas de directorio que hacen referencia a los exactos mismos datos subyacentes. Los datos en un archivo se especifican por su inode. Cada archivo en un sistema de archivos apunta a un inodo, pero no es necesario que cada archivo apunte a un inodo único, de ahí provienen los enlaces duros.

Dado que los inodos son únicos solo para un sistema de archivos dado, existe una limitación de que los enlaces duros deben estar en el mismo sistema de archivos (a diferencia de los enlaces simbólicos). Tenga en cuenta que, a diferencia de los enlaces simbólicos, no hay un archivo privilegiado, todos son iguales. El área de datos solo se liberará cuando se eliminen todos los archivos que usan ese inodo (y todos los procesos también lo cierran, pero ese es un problema diferente).

Puede usar el "ls -i"comando para obtener el inodo de un archivo en particular. Luego puede usar el "find <filesystemroot> -inum <inode>"comando para buscar todos los archivos en el sistema de archivos con ese inodo dado.

Aquí hay un script que hace exactamente eso. Lo invocas con:

findhardlinks ~/jquery.js

y encontrará todos los archivos en ese sistema de archivos que son enlaces duros para ese archivo:

pax@daemonspawn:~# ./findhardlinks /home/pax/jquery.js
Processing '/home/pax/jquery.js'
   '/home/pax/jquery.js' has inode 5211995 on mount point '/'
       /home/common/jquery-1.2.6.min.js
       /home/pax/jquery.js

Aquí está el guión.

#!/bin/bash
if [[ $# -lt 1 ]] ; then
    echo "Usage: findhardlinks <fileOrDirToFindFor> ..."
    exit 1
fi

while [[ $# -ge 1 ]] ; do
    echo "Processing '$1'"
    if [[ ! -r "$1" ]] ; then
        echo "   '$1' is not accessible"
    else
        numlinks=$(ls -ld "$1" | awk '{print $2}')
        inode=$(ls -id "$1" | awk '{print $1}' | head -1l)
        device=$(df "$1" | tail -1l | awk '{print $6}')
        echo "   '$1' has inode ${inode} on mount point '${device}'"
        find ${device} -inum ${inode} 2>/dev/null | sed 's/^/        /'
    fi
    shift
done

@pax: Parece que hay un error en el script. Lo comienzo . ./findhardlinks.bashmientras estoy en Zsh de OS X. Mi ventana actual en la pantalla se cierra.

44
@Masi El problema es tu inicial. (igual que el comando de origen). Eso hace que el comando exit 1 salga de su shell. Use chmod a + x findhardlinks.bash y luego ejecútelo con ./findhardlinks.bash o use bash findhardlinks.bash
njsf


3
Para hacer esto mediante programación, es probable que sea más resistente si se utiliza este lugar: INUM=$(stat -c %i $1). También NUM_LINKS=$(stat -c %h $1). Consulte man statpara ver más variables de formato que puede usar.
Joe

La mejor respuesta, de lejos. Prestigio.
MariusMatutiae

24
ls -l

La primera columna representará los permisos. La segunda columna será el número de subelementos (para directorios) o el número de rutas a los mismos datos (enlaces duros, incluido el archivo original) al archivo. P.ej:

-rw-r--r--@    2    [username]    [group]    [timestamp]     HardLink
-rw-r--r--@    2    [username]    [group]    [timestamp]     Original
               ^ Number of hard links to the data

2
Útil para determinar si un archivo dado tiene [otros] enlaces duros, pero no DÓNDE están.
mklement0

Además, no hay distinción técnica entre un enlace duro y un archivo original. Ambos son idénticos en el sentido de que simplemente apuntan a lo inodeque a su vez apunta al contenido del disco.
Guyarad

13

¿Qué tal el siguiente más simple? (¡Este último podría reemplazar los largos guiones de arriba!)

Si tiene un archivo específico <THEFILENAME>y desea conocer todos sus enlaces rígidos distribuidos en el directorio <TARGETDIR>(que incluso puede ser todo el sistema de archivos indicado por /)

find <TARGETDIR> -type f -samefile  <THEFILENAME>

Extendiendo la lógica, si desea conocer todos los archivos que <SOURCEDIR>tienen múltiples enlaces duros distribuidos en <TARGETDIR>:

find <SOURCEDIR> -type f -links +1   \
  -printf "\n\n %n HardLinks of file : %H/%f  \n"   \
  -exec find <TARGETDIR> -type f -samefile {} \; 

¡Esta es para mí la mejor respuesta! pero no lo usaría -type fporque el archivo también puede ser un directorio.
silvio

3
@silvio: solo puede crear enlaces duros a archivos , no directorios.
mklement0

@ mklement0: ¡Tienes razón!
silvio

Las entradas .y ..en los directorios son enlaces duros. Puede saber cuántos subdirectores hay en un directorio a partir del recuento de enlaces de .. Esto es discutible de todos modos, ya que find -samefile .todavía no imprimirá ningún subdir/..resultado. find(al menos la versión GNU) parece ser difícil de ignorar .., incluso con -noleaf.
Peter Cordes

también, esa idea de buscar todos los enlaces es O(n^2), y se ejecuta finduna vez para cada miembro de un conjunto de archivos vinculados. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separatefuncionaría, (16 no es lo suficientemente ancho para una representación decimal de 2 ^ 63-1, por lo que cuando su sistema de archivos XFS es lo suficientemente grande como para tener números de inodo tan altos, tenga cuidado)
Peter Cordes

5

Hay muchas respuestas con scripts para encontrar todos los enlaces duros en un sistema de archivos. La mayoría de ellos hacen cosas tontas como ejecutar find para escanear todo el sistema de archivos en -samefilebusca de CADA archivo con enlaces múltiples. Esto es Loco; todo lo que necesita es ordenar el número de inodo e imprimir duplicados.

Con solo una pasada sobre el sistema de archivos para buscar y agrupar todos los conjuntos de archivos enlazados

find dirs   -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
    sort -n | uniq -w 42 --all-repeated=separate

Esto es mucho más rápido que las otras respuestas para encontrar múltiples conjuntos de archivos enlazados.
find /foo -samefile /bares excelente para solo un archivo.

  • -xdev: límite a un sistema de archivos. No es estrictamente necesario ya que también imprimimos el FS-id para uniq en
  • ! -type drechazar directorios: las entradas .y ..significan que siempre están vinculados.
  • -links +1 : enlace cuenta estrictamente > 1
  • -printf ...imprime FS-id, número de inodo y ruta. (Con relleno para anchos de columna fijos de los que podemos hablar uniq).
  • sort -n | uniq ... ordenación numérica y uniquificación en las primeras 42 columnas, separando grupos con una línea en blanco

El uso ! -type d -links +1significa que la entrada de clasificación es tan grande como la salida final de uniq, por lo que no estamos haciendo una gran cantidad de clasificación de cadenas. A menos que lo ejecute en un subdirectorio que solo contiene uno de un conjunto de enlaces duros. De todos modos, esto usará MUCHO menos tiempo de CPU para volver a recorrer el sistema de archivos que cualquier otra solución publicada.

salida de muestra:

...
            2429             76732484 /home/peter/weird-filenames/test/.hiddendir/foo bar
            2429             76732484 /home/peter/weird-filenames/test.orig/.hiddendir/foo bar

            2430             17961006 /usr/bin/pkg-config.real
            2430             17961006 /usr/bin/x86_64-pc-linux-gnu-pkg-config

            2430             36646920 /usr/lib/i386-linux-gnu/dri/i915_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/i965_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/nouveau_vieux_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/r200_dri.so
            2430             36646920 /usr/lib/i386-linux-gnu/dri/radeon_dri.so
...

TODO ?: descomprime la salida con awko cut. uniqtiene un soporte de selección de campo muy limitado, por lo que relleno la salida de búsqueda y uso ancho fijo. 20 caracteres es lo suficientemente ancho para el máximo número posible de inodo o dispositivo (2 ^ 64-1 = 18446744073709551615). XFS elige los números de inodo según el lugar del disco en el que están asignados, no contiguamente desde 0, por lo que los sistemas de archivos XFS grandes pueden tener números de inodo> 32 bits incluso si no tienen miles de millones de archivos. Otros sistemas de archivos pueden tener números de inodo de 20 dígitos, incluso si no son gigantes.

TODO: ordena los grupos de duplicados por ruta. Tenerlos ordenados por punto de montaje y luego número de inodo mezcla cosas juntas, si tiene un par de subdirecciones diferentes que tienen muchos enlaces duros. (es decir, grupos de grupos dobles van juntos, pero la salida los mezcla).

Una final sort -k 3ordenaría las líneas por separado, no grupos de líneas como un solo registro. El preprocesamiento con algo para transformar un par de líneas nuevas en un byte NUL, y el uso de GNU sort --zero-terminated -k 3podría ser el truco. trSin embargo, solo funciona con caracteres individuales, no con patrones 2-> 1 o 1-> 2. perllo haría (o simplemente analizar y ordenar dentro de perl o awk). sedTambién podría funcionar.


1
%Des el identificador de sistema de archivos (que es único para el inicio actual, mientras que no hay sistemas de ficheros se umounted), por lo siguiente es incluso más genérica: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. Esto funciona siempre que ningún directorio dado contenga otro directorio en el nivel del sistema de archivos, también analiza todo lo que se puede vincular (como dispositivos o enlaces programables; sí, los enlaces programables pueden tener un recuento de enlaces mayor que 1). Tenga en cuenta que dev_ty ino_ttiene 64 bits de largo hoy. Esto probablemente se mantendrá mientras tengamos sistemas de 64 bits.
Tino

@Tino: gran punto sobre usar usando ! -type d, en lugar de -type f. Incluso tengo algunos enlaces simbólicos enlazados en mi sistema de archivos por organizar algunas colecciones de archivos. Actualicé mi respuesta con su versión mejorada (pero puse el id de fs primero, por lo que el orden de clasificación al menos se agrupa por sistema de archivos)
Peter Cordes

3

Esto es algo así como un comentario a la propia respuesta y guión de Torocoro-Macho, pero obviamente no cabe en el cuadro de comentarios.


Reescribió su script con formas más directas de encontrar la información y, por lo tanto, muchas menos invocaciones de proceso.

#!/bin/sh
xPATH=$(readlink -f -- "${1}")
for xFILE in "${xPATH}"/*; do
    [ -d "${xFILE}" ] && continue
    [ ! -r "${xFILE}" ] && printf '"%s" is not readable.\n' "${xFILE}" 1>&2 && continue
    nLINKS=$(stat -c%h "${xFILE}")
    if [ ${nLINKS} -gt 1 ]; then
        iNODE=$(stat -c%i "${xFILE}")
        xDEVICE=$(stat -c%m "${xFILE}")
        printf '\nItem: %s[%d] = %s\n' "${xDEVICE}" "${iNODE}" "${xFILE}";
        find "${xDEVICE}" -inum ${iNODE} -not -path "${xFILE}" -printf '     -> %p\n' 2>/dev/null
    fi
done

Traté de mantenerlo lo más similar posible al suyo para facilitar la comparación.

Comentarios sobre este script y el tuyo

  • Uno siempre debe evitar la $IFSmagia si un globo es suficiente, ya que es innecesariamente complicado, y los nombres de los archivos pueden contener nuevas líneas (pero en la práctica, principalmente, la primera razón).

  • Debe evitar el análisis manual lsy la salida de este tipo tanto como sea posible, ya que tarde o temprano lo morderá. Por ejemplo: en su primera awklínea, falla en todos los nombres de archivos que contienen espacios.

  • printfa menudo ahorrará problemas al final ya que es muy robusto con la %ssintaxis. También le brinda control total sobre la salida, y es consistente en todos los sistemas, a diferencia echo.

  • stat puede ahorrarle mucha lógica en este caso.

  • GNU find es poderoso

  • Sus invocaciones heady tailpodrían haberse manejado directamente awkcon, por ejemplo, el exitcomando y / o la selección en la NRvariable. Esto ahorraría invocaciones de procesos, que casi siempre mejoran severamente el rendimiento en scripts de trabajo duro.

  • Tu egreps bien podría ser justo grep.


xDEVICE = $ (stat -c% m "$ {xFILE}") no funciona en todos los sistemas (por ejemplo: stat (GNU coreutils) 6.12). Si el script genera "Elemento:?" al frente de cada línea, luego reemplace esta línea ofensiva con una línea más parecida al guión original, pero con xITEM renombrado a xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien

Si solo desea grupos de enlaces duros, en lugar de repetirlos con cada miembro como "maestro", use find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. Esto es MUCHO más rápido, ya que solo atraviesa el fs una vez. Para múltiples FSes a la vez, necesitará prefijar los números de inodo con una identificación FS. Quizás confind -exec stat... -printf ...
Peter Cordes

convirtió esa idea en una respuesta
Peter Cordes

2

Basado en el findhardlinksscript (renombrado como hard-links), esto es lo que he refactorizado y lo hice funcionar.

Salida:

# ./hard-links /root

Item: /[10145] = /root/.profile
    -> /proc/907/sched
    -> /<some-where>/.profile

Item: /[10144] = /root/.tested
    -> /proc/907/limits
    -> /<some-where else>/.bashrc
    -> /root/.testlnk

Item: /[10144] = /root/.testlnk
    -> /proc/907/limits
    -> /<another-place else>/.bashrc
    -> /root/.tested

 

# cat ./hard-links
#!/bin/bash
oIFS="${IFS}"; IFS=$'\n';
xPATH="${1}";
xFILES="`ls -al ${xPATH}|egrep "^-"|awk '{print $9}'`";
for xFILE in ${xFILES[@]}; do
  xITEM="${xPATH}/${xFILE}";
  if [[ ! -r "${xITEM}" ]] ; then
    echo "Path: '${xITEM}' is not accessible! ";
  else
    nLINKS=$(ls -ld "${xITEM}" | awk '{print $2}')
    if [ ${nLINKS} -gt 1 ]; then
      iNODE=$(ls -id "${xITEM}" | awk '{print $1}' | head -1l)
      xDEVICE=$(df "${xITEM}" | tail -1l | awk '{print $6}')
      echo -e "\nItem: ${xDEVICE}[$iNODE] = ${xITEM}";
      find ${xDEVICE} -inum ${iNODE} 2>/dev/null|egrep -v "${xITEM}"|sed 's/^/   -> /';
    fi
  fi
done
IFS="${oIFS}"; echo "";

Publiqué comentarios sobre este script como una respuesta separada.
Daniel Andersson

1

Una solución GUI se acerca mucho a su pregunta:

No puede enumerar los archivos vinculados reales de "ls" porque, como han señalado los comentaristas anteriores, los "nombres" de los archivos son meros alias de los mismos datos. Sin embargo, en realidad hay una herramienta GUI que se acerca mucho a lo que desea, que es mostrar una lista de rutas de nombres de archivos que apuntan a los mismos datos (como enlaces duros) en Linux, se llama FSLint. La opción que desea está en "Choques de nombres" -> deseleccione "casilla de verificación $ RUTA" en Buscar (XX) -> y seleccione "Alias" en el cuadro desplegable después de "para ..." hacia la parte superior central.

FSLint está muy poco documentado, pero descubrí que asegurándose de que el árbol de directorios limitado en "Ruta de búsqueda" con la casilla de verificación seleccionada para "¿Recurrir?" y las opciones antes mencionadas, una lista de datos vinculados con rutas y nombres que "apuntan" a los mismos datos se producen después de las búsquedas del programa.


FSlint se puede encontrar en pixelbeat.org/fslint
mklement0

1

Puede configurar lspara resaltar los enlaces duros usando un 'alias', pero como se indicó anteriormente, no hay forma de mostrar la 'fuente' del enlace duro, por lo que anexo .hardlinkayuda con eso.

resaltar enlaces duros

Agregue lo siguiente en algún lugar de su .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.