Comment pouvez-vous voir le lien dur réel par ls?

97

je cours

ln /a/A /b/B

Je voudrais voir dans le dossier aoù le fichier A pointe par ls.

Léo Léopold Hertz
la source
1
Les liens physiques ne sont pas des pointeurs, mais les liens symboliques. Ce sont plusieurs noms pour le même fichier (inode). Après un link(2)appel système, il n'y a aucun sens en ce qui concerne l'un est l'original et l'autre est le lien. C'est pourquoi, comme le soulignent les réponses, le seul moyen de trouver tous les liens est find / -samefile /a/A. Parce qu'une entrée de répertoire pour un inode ne "sait pas" d'autres entrées de répertoire pour le même inode. Ils ne font que recompter l'inode afin qu'il puisse être supprimé lorsque son nom de famille le désigne unlink(2)ed. (Ceci est le "nombre de liens" en lssortie).
Peter Cordes
@PeterCordes: Le nombre de références est-il réellement stocké DANS l'entrée de lien physique? C’est ce que votre langage implique ("Ils ne font que redécumer l’inode ..."). Cela n’aurait aucun sens si les liens ne connaissaient rien les uns des autres, puisqu’à chaque mise à jour, tous les autres devraient être mis à jour. Ou le refcount est-il stocké dans l'inode lui-même? (Pardonnez-moi si c'est une question idiote, je me considère comme un débutant et j'apprends toujours).
Loneboat
1
Le refcount est stocké dans l'inode, comme vous avez fini par le comprendre, à partir des autres faits. :) Les entrées de répertoire sont nommées pointeurs sur les inodes. Nous appelons cela "liaison difficile" lorsque vous avez plusieurs noms pointant vers le même inode.
Peter Cordes

Réponses:

171

Vous pouvez trouver le numéro d'inode de votre fichier avec

ls -i

et

ls -l

affiche le nombre de références (nombre de liens durs vers un inode particulier)

Après avoir trouvé le numéro d'inode, vous pouvez rechercher tous les fichiers avec le même inode:

find . -inum NUM

affichera les noms de fichiers pour l’inode NUM dans le répertoire courant (.)

zzr
la source
46
vous pourriez juste courir trouver. -samefile nomfichier
BeowulfNode42
1
@ BeowulfNode42 Cette commande est excellente, mais elle nécessite au moins le dossier racine partagé des mêmes fichiers.
Itachi
1
cette réponse donne un "faire ceci" pragmatique mais je suis fermement convaincu que @LaurenceGonsalves répond au "comment" et / ou au "pourquoi".
Trevor Boyd Smith
65

Il n'y a pas vraiment de réponse bien définie à votre question. Contrairement aux liens symboliques, les liens physiques ne peuvent être distingués du "fichier d'origine".

Les entrées de répertoire consistent en un nom de fichier et un pointeur sur un inode. L'inode contient à son tour les métadonnées du fichier et (pointe vers) le contenu réel du fichier). La création d'un lien dur crée un autre nom de fichier + référence au même inode. Ces références sont unidirectionnelles (du moins dans les systèmes de fichiers classiques) - l'inode ne conserve qu'un nombre de références. Il n'y a pas de moyen intrinsèque de savoir quel est le nom de fichier "original".

À propos, c’est la raison pour laquelle l’appel système à "supprimer" un fichier est appelé unlink. Cela supprime simplement un lien dur. L'inode et les données attachées ne sont supprimés que si le compte de références de l'inode passe à 0.

Le seul moyen de trouver les autres références à un inode donné consiste à effectuer une recherche exhaustive dans le système de fichiers en vérifiant quels fichiers font référence à l'inode en question. Vous pouvez utiliser 'test A -ef B' à partir du shell pour effectuer cette vérification.

Laurence Gonsalves
la source
35
Cela signifie qu’il n’existe pas de lien physique vers un autre fichier , le fichier original étant également un lien physique; Les liens physiques pointent vers un emplacement sur le disque .
jtbandes
12
@jtbandes: les liens physiques pointent vers un inode qui pointe vers les données réelles.
dash17291
33

UNIX a des liens durs et des liens symboliques (créés avec "ln"et "ln -s"respectivement). Les liens symboliques sont simplement un fichier contenant le véritable chemin d'accès à un autre fichier et pouvant traverser des systèmes de fichiers.

Des liens solides existent depuis les tout premiers jours d'UNIX (je me souviens de toute façon, et cela remonte assez longtemps). Ce sont deux entrées de répertoire qui référencent exactement les mêmes données sous-jacentes. Les données dans un fichier sont spécifiées par son inode. Chaque fichier d’un système de fichiers pointe vers un inode, mais il n’est pas nécessaire que chaque fichier pointe vers un inode unique - c’est de là que proviennent les liens physiques.

Comme les inodes ne sont uniques que pour un système de fichiers donné, il existe une limitation selon laquelle les liens durs doivent se trouver sur le même système de fichiers (contrairement aux liens symboliques). Notez que, contrairement aux liens symboliques, il n'y a pas de fichier privilégié - ils sont tous égaux. La zone de données ne sera publiée que lorsque tous les fichiers utilisant cet inode seront supprimés (et tous les processus le fermeront également, mais le problème est différent).

Vous pouvez utiliser la "ls -i"commande pour obtenir l'inode d'un fichier particulier. Vous pouvez ensuite utiliser la "find <filesystemroot> -inum <inode>"commande pour rechercher tous les fichiers du système de fichiers avec cet inode donné.

Voici un script qui fait exactement cela. Vous l'invoquez avec:

findhardlinks ~/jquery.js

et il trouvera tous les fichiers sur ce système de fichiers qui sont des liens durs pour ce fichier:

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

Voici le script.

#!/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

la source
@pax: Il semble y avoir un bug dans le script. Je commence par . ./findhardlinks.bashêtre dans Zsh d’OS X. Ma fenêtre actuelle dans l'écran se ferme.
4
@Masi Le problème est votre initiale. (identique à la commande source). Cela provoque la commande exit 1 pour quitter votre shell. Utilisez chmod a + x findhardlinks.bash puis exécutez-le avec ./findhardlinks.bash ou utilisez bash findhardlinks.bash
njsf
Veuillez consulter ma réponse à votre réponse sur superuser.com/questions/12972/to-see-hardlinks-by-ls/…
Léo Léopold Hertz
3
Pour ce faire , un programme, il est probablement plus résistante si vous utilisez ceci: INUM=$(stat -c %i $1). Aussi NUM_LINKS=$(stat -c %h $1). Voir man statpour plus de variables de format que vous pouvez utiliser.
Joe
Meilleure réponse, de loin. Gloire.
MariusMatutiae
24
ls -l

La première colonne représentera les autorisations. La deuxième colonne sera le nombre de sous-éléments (pour les répertoires) ou le nombre de chemins d'accès aux mêmes données (liens physiques, y compris le fichier d'origine) vers le fichier. Par exemple:

-rw-r--r--@    2    [username]    [group]    [timestamp]     HardLink
-rw-r--r--@    2    [username]    [group]    [timestamp]     Original
               ^ Number of hard links to the data
paupière
la source
2
Utile pour déterminer SI un fichier donné a [autres] liens physiques, mais pas OU ils sont.
mklement0
En outre, il n'y a pas de distinction technique entre un fichier de lien dur et un fichier d'origine. Ils sont tous les deux identiques en ce sens qu’ils indiquent simplement le inodecontenu du disque.
Guyarad
13

Que diriez-vous du plus simple suivant? (Ce dernier pourrait remplacer les longs scripts ci-dessus!)

Si vous avez un fichier spécifique <THEFILENAME>et que vous voulez connaître tous ses liens physiques disséminés dans le répertoire <TARGETDIR>(ce qui peut même être le système de fichiers complet indiqué par /)

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

En étendant la logique, si vous voulez connaître tous les fichiers dans les <SOURCEDIR>multiples liens durs répartis sur <TARGETDIR>:

find <SOURCEDIR> -type f -links +1   \
  -printf "\n\n %n HardLinks of file : %H/%f  \n"   \
  -exec find <TARGETDIR> -type f -samefile {} \; 
Aime la probabilité
la source
C'est pour moi la meilleure réponse! mais je ne voudrais pas utiliser -type fparce que le fichier peut être un répertoire aussi.
Silvio
3
@silvio: Vous ne pouvez créer que des liens physiques vers des fichiers , pas des répertoires.
mklement0
@ mklement0: Vous avez raison!
Silvio
Les entrées .et ..dans les répertoires sont des liens physiques. Vous pouvez savoir combien de sous-répertoires se trouvent dans un répertoire à partir du nombre de liens de .. C'est de toute façon discutable, car find -samefile .n'imprimera toujours pas de subdir/..sortie. find(du moins la version GNU) semble être difficile à ignorer .., même avec -noleaf.
Peter Cordes
En outre, cette idée de recherche de tous les liens existe O(n^2)et s'exécute findune fois pour chaque membre d'un ensemble de fichiers liés. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separatepourrait fonctionner (16 n'est pas assez large pour une représentation décimale de 2 ^ 63-1, donc quand votre système de fichiers XFS est assez grand pour avoir un nombre d'inodes aussi élevé, faites attention)
Peter Cordes
5

Il y a beaucoup de réponses avec des scripts pour trouver tous les liens durs dans un système de fichiers. La plupart d'entre eux font des bêtises, comme exécuter find, pour analyser tout le système de fichiers à la -samefilerecherche de chaque fichier à liaisons multiples. C'est fou; tout ce dont vous avez besoin est de trier le numéro d'inode et d'imprimer les doublons.

Avec un seul passage sur le système de fichiers pour rechercher et regrouper tous les ensembles de fichiers liés

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

C'est beaucoup plus rapide que les autres réponses pour trouver plusieurs jeux de fichiers en liens durs.
find /foo -samefile /barest excellent pour un seul fichier.

  • -xdev: limite à un système de fichiers. Non strictement nécessaire car nous imprimons également le FS-id sur uniq on
  • ! -type drefuser les répertoires: les entrées .et ..signifient qu'elles sont toujours liées.
  • -links +1 : nombre de liens strictement > 1
  • -printf ...print FS-id, le numéro d'inode et le chemin. (Avec un rembourrage à des largeurs de colonne fixes dont on peut parler uniq.)
  • sort -n | uniq ... trie et identifie numériquement sur les 42 premières colonnes en séparant les groupes par une ligne vide

Utiliser ! -type d -links +1signifie que l'entrée de ce tri est seulement aussi grande que la sortie finale de uniq, nous ne faisons donc pas beaucoup de tri de chaînes. Sauf si vous l'exécutez dans un sous-répertoire contenant uniquement l'un des liens physiques. Quoi qu'il en soit, cela utilisera BEAUCOUP moins de temps processeur pour traverser le système de fichiers que toute autre solution publiée.

exemple de sortie:

...
            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?: Décompressez la sortie avec awkou cut. uniqa un support de sélection de champ très limité, aussi j’ai rempli le résultat de la recherche et utilisé une largeur fixe. 20chars est suffisamment large pour le nombre maximal d'inode ou de périphérique possible (2 ^ 64-1 = 18446744073709551615). XFS choisit les numéros d'inode en fonction de l'endroit où ils sont alloués sur le disque, et non de manière contiguë à 0, afin que les systèmes de fichiers XFS volumineux puissent avoir des numéros d'inode> 32 bits même s'ils ne contiennent pas des milliards de fichiers. Les autres systèmes de fichiers peuvent avoir des numéros d'inodes à 20 chiffres même s'ils ne sont pas gigantesques.

TODO: trier les groupes de doublons par chemin. Si vous les triez par point de montage, le numéro d’inode mélange les éléments si vous avez plusieurs sous-répertoires différents qui comportent de nombreux liens fixes. (c'est-à-dire que des groupes de groupes de dup vont ensemble, mais que le résultat les mélange)

Une finale sort -k 3trierait les lignes séparément, pas les groupes de lignes comme un seul enregistrement. Un prétraitement avec quelque chose qui transforme une paire de nouvelles lignes en un octet NUL, et l’utilisation de GNU sort --zero-terminated -k 3pourraient faire l’essentiel. trne fonctionne que sur des caractères simples, pas 2-> 1 ou 1-> 2 motifs, cependant. perlle ferait (ou simplement analyser et trier perl ou awk). sedpourrait aussi fonctionner.

Peter Cordes
la source
1
%Dest l'identifiant du système de fichiers (il est unique pour le démarrage en cours alors qu'aucun des systèmes de fichiers sont umountndlr), donc qui suit est encore plus générique: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. Cela fonctionne tant qu'aucun répertoire donné ne contient un autre répertoire au niveau du système de fichiers, il examine également tout ce qui peut être lié de manière permanente (comme des périphériques ou des liens symboliques - oui, les liens symboliques peuvent avoir un nombre de liens supérieur à 1). Notez que dev_tet ino_test de 64 bits de long aujourd'hui. Cela tiendra probablement aussi longtemps que nous aurons des systèmes 64 bits.
Tino
@Tino: excellent point pour utiliser ! -type d, au lieu de -type f. J'ai même quelques liens symboliques liés sur mon système de fichiers pour organiser certaines collections de fichiers. Mise à jour de ma réponse avec votre version améliorée (mais je mets d'abord le fs-id, donc l'ordre de tri groupe au moins par système de fichiers.)
Peter Cordes
3

Ceci est en quelque sorte un commentaire de la réponse et du script de Torocoro-Macho, mais cela ne rentrera évidemment pas dans la zone de commentaire.


Réécrivez votre script avec des moyens plus simples pour trouver l'information, et ainsi beaucoup moins d'appels de processus.

#!/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

J'ai essayé de le garder aussi semblable que possible au vôtre pour faciliter la comparaison.

Commentaires sur ce script et le vôtre

  • Il faut toujours éviter la $IFSmagie si un glob suffit, car il est inutilement compliqué, et les noms de fichiers peuvent en réalité contenir des nouvelles lignes (mais, dans la pratique, principalement la première raison).

  • Évitez lsautant que possible d' analyser manuellement ce type de sortie, car elle vous mordra tôt ou tard. Par exemple: dans votre première awkligne, vous échouez sur tous les noms de fichiers contenant des espaces.

  • printfsera souvent sauver des problèmes à la fin car il est si robuste avec la %ssyntaxe. Elle vous donne également un contrôle total sur la sortie et est uniforme sur tous les systèmes, à la différence echo.

  • stat peut vous faire économiser beaucoup de logique dans ce cas.

  • GNU find est puissant.

  • Vos invocations headet tailauraient pu être traitées directement awkavec par exemple la exitcommande et / ou la sélection sur la NRvariable. Cela permettrait d'économiser les invocations de processus, qui améliorent presque toujours les performances dans des scripts laborieux.

  • Votre egreps pourrait tout aussi bien être juste grep.

Daniel Andersson
la source
xDEVICE = $ (stat -c% m "$ {xFILE}") ne fonctionne pas sur tous les systèmes (par exemple: stat (GNU coreutils) 6.12). Si le script génère "Item:?" au début de chaque ligne, remplacez cette ligne par une ligne plus semblable au script d'origine, mais avec xITEM renommé en xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien
Si vous voulez juste des groupes de liens durs, plutôt que de les répéter avec chaque membre comme "maître", utilisez find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. C’EST BEAUCOUP plus rapide, car il ne traverse le fs qu’une fois. Pour plusieurs FS à la fois, vous devez préfixer les numéros d'inode avec un ID de FS. Peut-être avecfind -exec stat... -printf ...
Peter Cordes
a transformé cette idée en réponse
Peter Cordes
2

Basé sur le findhardlinksscript (renommé en hard-links), c’est ce que j’ai refactoré et fait fonctionner.

Sortie:

# ./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 "";
Torocoro-Macho
la source
J'ai posté des commentaires sur ce script en tant que réponse séparée.
Daniel Andersson
1

Une solution graphique se rapproche beaucoup de votre question:

Vous ne pouvez pas répertorier les fichiers liés réels à partir de "ls" car, comme l'ont souligné des commentateurs précédents, les fichiers "noms" ne sont que de simples alias pour les mêmes données. Cependant, il existe en fait un outil graphique très proche de ce que vous voulez, qui consiste à afficher une liste de chemins de noms de fichiers pointant vers les mêmes données (en tant que liens durs) sous linux, il s’appelle FSLint. L'option souhaitée se trouve sous "Nom de conflit" -> désélectionnez "case à cocher $ CHEMIN" dans Recherche (XX) -> et sélectionnez "Alias" dans la liste déroulante après "pour ..." vers le haut, au milieu.

FSLint est très mal documenté, mais j’ai trouvé cela en vérifiant que l’arborescence de répertoires limitée était sous "Chemin de recherche" avec la case à cocher sélectionnée pour "Recurse?" et les options susmentionnées, une liste de données en liaison fixe avec des chemins et des noms qui "pointent" sur les mêmes données sont générées après la recherche du programme.

Charles
la source
FSlint peut être trouvé à pixelbeat.org/fslint
mklement0
1

Vous pouvez configurer la lsmise en surbrillance des liens physiques en utilisant un "alias", mais comme indiqué précédemment, il n’existe aucun moyen de montrer la "source" du lien physique, c’est pourquoi j’ajoute .hardlinkpour vous aider.

mettre en évidence les liens durs

Ajoutez ce qui suit quelque part dans votre .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Daniel Sokolowski
la source