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


je cours

ln /a/A /b/B

Je voudrais voir au dossier a où le fichier A pointe par ls.


77
2017-07-25 09:41


origine


Les liens durs ne sont pas des pointeurs, les liens symboliques le sont. Ils sont plusieurs noms pour le même fichier (inode). Après un link(2) appel système, il n'y a aucun sens dans lequel est l'original et l'autre est le lien. C'est pourquoi, comme le soulignent les réponses, la seule façon de trouver tous les liens est find / -samefile /a/A. Parce qu'une entrée de répertoire pour un inode ne "connaît" pas les autres entrées de répertoire pour le même inode. Tout ce qu’ils font, c’est de recaler l’inode pour pouvoir la supprimer lorsque le nom de famille est unlink(2)ed. (Ceci est le "nombre de liens" dans ls sortie). - Peter Cordes
@PeterCordes: Le refcount est-il réellement stocké dans l'entrée du lien dur? C'est ce que votre formulation implique ("Tout ce qu’ils font, c’est de recomposer l’inode ...") Mais cela n’aurait pas de sens si les liens ne connaissaient rien les uns des autres, car quand on mettait à jour être mis à jour Ou le refcount est-il stocké dans l'inode lui-même? (Pardonnez-moi si c'est une question stupide, je me considère comme un débutant et j'apprends encore). - loneboat
Le refcount est stocké dans l'inode, comme vous l'avez éventuellement compris, à partir des autres faits. :) Les entrées du répertoire sont nommées pointeurs vers inodes. Nous l'appelons "liaison dure" lorsque vous avez plusieurs noms pointant vers le même inode. - Peter Cordes


Réponses:


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 NUM inode dans le répertoire actuel (.)


140
2017-07-25 10:01



vous pourriez juste courir trouver. -samefile nomfichier - BeowulfNode42
@ BeowulfNode42 Cette commande est géniale, mais elle nécessite au moins le dossier racine partagé des mêmes fichiers. - Itachi
cette réponse donne un "faire ceci" pragmatique mais je sens fortement que @LaurenceGonsalves répond les questions "comment" et / ou "pourquoi". - Trevor Boyd Smith


Il n'y a pas vraiment de réponse bien définie à votre question. Contrairement aux liens symboliques, les liens physiques ne se distinguent pas du "fichier original".

Les entrées de répertoire se composent d'un nom de fichier et d'un pointeur vers un inode. L'inode contient à son tour les métadonnées du fichier et (les pointeurs vers) le contenu réel du fichier). Créer un lien dur crée un autre nom de fichier + référence au même inode. Ces références sont unidirectionnelles (dans les systèmes de fichiers typiques, au moins) - l'inode conserve uniquement un compte de référence. Il n'y a pas de moyen intrinsèque de découvrir quel est le nom de fichier "original".

Au fait, c'est pourquoi l'appel système pour "supprimer" un fichier s'appelle unlink. Il supprime juste un lien dur. L'inode et les données jointes sont supprimées uniquement si le compte de référence de l'inode tombe à 0.

La seule façon de trouver les autres références à un inode donné consiste à effectuer une recherche exhaustive sur 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' depuis le shell pour effectuer cette vérification.


52
2017-07-25 09:51



Cela signifie que il n'existe pas de lien vers un autre fichier, comme le fichier original est également un lien dur; les liens durs pointent vers un emplacement sur le disque. - jtbandes
@jtbandes: les liens durs pointent vers un inode qui pointe vers les données réelles. - dash17291


UNIX possède des liens physiques et des liens symboliques (créés avec "ln" et "ln -s" respectivement). Les liens symboliques sont simplement un fichier contenant le chemin réel vers un autre fichier et pouvant traverser des systèmes de fichiers.

Les liens durs existent depuis les premiers jours d'UNIX (et je m'en souviens quand même, et cela remonte à un moment). Ce sont deux entrées de répertoire qui référencent le exact mêmes données sous-jacentes. Les données d'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-à-dire d'où proviennent les liens physiques.

Comme les inodes ne sont uniques que pour un système de fichiers donné, il existe une limitation: les liens physiques doivent être 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 libérée que lorsque tout les fichiers utilisant cet inode sont supprimés (et tous les processus le ferment aussi, mais c'est un problème différent).

Vous pouvez utiliser le "ls -i" commande pour obtenir l'inode d'un fichier particulier. Vous pouvez alors utiliser le "find <filesystemroot> -inum <inode>" commande pour trouver tous les fichiers sur le 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

31
2017-07-25 12:13



@pax: Il semble y avoir un bug dans le script. Je commence par . ./findhardlinks.bash tout en étant dans Zsh OS X. Ma fenêtre actuelle dans Écran se ferme.
@Masi La question est votre initiale. (identique à la commande source). Cela fait que la commande exit 1 quitte votre shell. Utilisez chmod a + x findhardlinks.bash puis exécutez-le avec ./findhardlinks.bash ou utilisez bash findhardlinks.bash - njsf
S'il vous plaît, voir ma réponse à votre réponse à superuser.com/questions/12972/to-see-hardlinks-by-ls/... - Léo Léopold Hertz 준영
Pour ce faire par programmation, il est probablement plus résistant si vous l'utilisez à la place: INUM=$(stat -c %i $1). Aussi NUM_LINKS=$(stat -c %h $1). Voir man stat pour plus de variables de format, vous pouvez utiliser. - Joe
Meilleure réponse, de loin. Gloire. - MariusMatutiae


ls -l

La première colonne représente les autorisations. La deuxième colonne sera le nombre de sous-éléments (pour les répertoires) ou le nombre de chemins vers les mêmes données (liens matériels, 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

22
2017-07-25 10:01



Utile pour déterminer si un fichier donné possède [d'autres] liens matériels, mais pas où ils sont. - mklement0


Qu'en est-il du plus simple suivant? (Le dernier pourrait remplacer les longs scripts ci-dessus!)

Si vous avez un fichier spécifique <THEFILENAME>et veulent connaître tous ses liens durs répartis dans l'annuaire <TARGETDIR>, (qui peut même être le système de fichiers entier noté /)

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

En étendant la logique, si vous souhaitez connaître tous les fichiers du <SOURCEDIR> avoir plusieurs 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 {} \; 

8
2017-08-15 08:52



C'est pour moi la meilleure réponse! mais je ne voudrais pas utiliser -type f car le fichier peut aussi être un répertoire. - silvio
@silvio: vous ne pouvez créer que des liens durs vers des dossiers, pas des répertoires. - mklement0
@ mklement0: Vous avez raison! - silvio
le . et .. les entrées dans les répertoires sont des liens durs. Vous pouvez dire combien de sous-répertoires sont dans un répertoire à partir du nombre de liens de .. Ceci est discutable de toute façon, depuis find -samefile . ne sera toujours pas imprimer subdir/.. sortie. find (au moins la version GNU) semble être codée pour ignorer .., même avec -noleaf. - Peter Cordes
En outre, cette idée de recherche de tous les liens est O(n^2)et court find une fois pour chaque membre d'un ensemble de fichiers liés find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate fonctionnerait, (16 n'est pas assez large pour une représentation décimale de 2 ^ 63-1, donc lorsque votre système de fichiers XFS est assez grand pour avoir des numéros d'inode aussi élevés, faites attention) - Peter Cordes


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 choses idiotes, comme lancer un scan pour analyser tout le système de fichiers. -samefile pour 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.

find directories.. -xdev ! -type d -links +1 -printf '%20D %20i %p\n' | sort -n | uniq -w 42 --all-repeated=separate  (Merci à @Tino pour avoir modifié ma commande originale afin de prendre en charge un identifiant FS-id (%D), et pour gérer tous les types de fichiers non-répertoire, pas seulement les fichiers réguliers. Cela trouvera vos liens symboliques, tuyaux, etc.)

En utilisant ! -type d -links +1 signifie que l'entrée du tri est aussi grande que la sortie finale de uniq. Sauf si vous l'exécutez sur un sous-répertoire contenant uniquement un ensemble de liens durs. Quoi qu'il en soit, cela va utiliser beaucoup moins de temps processeur pour repasser le système de fichiers que toute autre solution publiée.

sortie de l'échantillon:

...
            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é-pad la sortie. uniq dispose d'un support de sélection de champ très limité, donc je compresse la sortie de recherche et utilise une largeur fixe. 20chars est assez large pour le nombre maximum possible d'inodes ou de périphériques (2 ^ 64-1 = 18446744073709551615). XFS choisit les numéros d'inode en fonction de l'emplacement sur le disque auquel ils sont alloués, et non de façon contiguë à partir de 0. Les systèmes de fichiers XFS de grande taille peuvent avoir des numéros d'inode> 32 bits même s'ils ne contiennent pas des milliards de fichiers. D'autres systèmes de fichiers peuvent avoir des numéros d'inode à 20 chiffres, même s'ils ne sont pas gigantesques.

TODO: trie les groupes de doublons par chemin. En les triant par point de montage, le numéro d'inode mélange les choses, si vous avez deux sous-répertoires différents qui ont beaucoup de liens durs. (c’est-à-dire que des groupes de groupes de dup vont de pair, mais la sortie les mélange).

Une finale sort -k 3 trierait les lignes séparément, pas les groupes de lignes en un seul enregistrement. Prétraitement avec quelque chose pour transformer une paire de nouvelles lignes en un octet NUL et en utilisant GNU sort --zero-terminated -k 3 pourrait faire l'affaire. tr ne fonctionne que sur des caractères uniques, mais pas sur les modèles 2-> 1 ou 1-> 2. perl le ferait (ou simplement analyser et trier dans perl ou awk). sed pourrait aussi fonctionner.


4
2018-04-21 19:32



%D est l'identifiant du système de fichiers (il est unique pour le démarrage en cours alors qu'aucun système de fichiers n'est umounted), la suite 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 aussi longtemps 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 relié (comme les périphériques ou les liens logiciels - oui, les liens logiciels peuvent avoir un nombre supérieur à 1). Notez que dev_t et ino_t est de 64 bits de long aujourd'hui. Cela tiendra probablement tant que nous avons des systèmes 64 bits. - Tino
@Tino: bon point sur l'utilisation de l'utilisation ! -type d, au lieu de -type f. J'ai même des liens symboliques sur mon système de fichiers pour organiser des collections de fichiers. Mise à jour de ma réponse avec votre version améliorée (mais je mets d'abord l'identifiant fs-id, donc l'ordre de tri regroupe au moins les groupes par système de fichiers). - Peter Cordes


Ceci est en quelque sorte un commentaire de la propre réponse et du propre script de Torocoro-Macho, mais il ne sera évidemment pas adapté à la boîte de commentaire.


Réécrivez votre script avec des moyens plus simples pour trouver les informations, et donc 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 proche que possible du vôtre pour faciliter la comparaison.

Commentaires sur ce script et le vôtre

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

  • Vous devriez éviter d'analyser manuellement ls et une telle sortie autant que possible, car elle vous mordra tôt ou tard. Par exemple: dans votre premier awkline, vous échouez sur tous les noms de fichiers contenant des espaces.

  • printf sauvera souvent des problèmes à la fin car il est si robuste avec le %s syntaxe. Il vous donne également un contrôle total sur la sortie et est cohérent à travers tout systèmes, contrairement à echo.

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

  • GNU find est puissant.

  • Votre head et tail les invocations auraient pu être traitées directement dans awk avec par ex. la exit commande et / ou sélection sur le NR variable. Cela sauverait des invocations de processus, ce qui améliore presque toujours les performances des scripts.

  • Votre egreps pourrait juste être juste grep.


3
2018-06-13 07:40



xDEVICE = $ (stat -c% m "$ {xFILE}") ne fonctionne pas sur tous les systèmes (par exemple: stat (GNU coreutils) 6.12). Si le script affiche "Item:?" au début de chaque ligne, remplacez cette ligne par une ligne plus proche du script original, 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. Ceci est BEAUCOUP plus rapide, car il ne traverse que la première fois. Pour plusieurs FS à la fois, vous devez préfixer les numéros d'inode avec un identifiant FS. Peut-être avec find -exec stat... -printf ... - Peter Cordes
transformé cette idée en réponse - Peter Cordes


Basé sur findhardlinks script (renommé à hard-links), c'est ce que j'ai refait 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 "";

2
2017-11-16 22:46



J'ai posté des commentaires sur ce script en tant que réponse séparée. - Daniel Andersson


Une solution GUI se rapproche vraiment de votre question:

Vous ne pouvez pas lister les fichiers réels liés par des liens solides de "ls" car, comme l'ont fait remarquer les commentateurs précédents, les "noms" de fichiers ne sont que des alias des mêmes données. Cependant, il existe en fait un outil graphique qui se rapproche vraiment de ce que vous voulez, à savoir afficher la liste des noms de fichiers qui pointent vers les mêmes données (sous forme de liens durs) sous Linux, c'est FSLint. L'option que vous voulez est sous "Conflits de noms" -> désélectionnez "case à cocher $ PATH" dans Recherche (XX) -> et sélectionnez "Alias" dans la liste déroulante après "pour ..." vers le haut.

FSLint est très mal documenté mais j'ai constaté que l'arborescence de répertoires limitée sous "Chemin de recherche" avec la case à cocher sélectionnée pour "Recurse?" et les options mentionnées ci-dessus, une liste de données liées par un lien avec des chemins et des noms qui "pointent" vers les mêmes données sont produites après les recherches de programme.


1
2018-01-20 18:00



FSlint peut être trouvé à pixelbeat.org/fslint - mklement0


Vous pouvez configurer ls pour mettre en évidence les liens durs en utilisant un «alias», mais comme indiqué précédemment, il est impossible de montrer la «source» du lien physique.

ls highlighting hardlinks

Ajoutez ce qui suit quelque part dans votre .bashrc

#alias ll='LS_COLORS="mh=1;37" ls -l'
alias la='LS_COLORS="mh=1;37" ls -A'
alias l='LS_COLORS="mh=1;37" ls -CF'
alias ll='LC_COLLATE=C LS_COLORS="mh=1;37" ls -lA --si --group-directories-first'

1
2017-12-06 17:34