web-dev-qa-db-fra.com

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

Je cours

ln /a/A /b/B

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

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 (.)

162
zzr

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".

En passant, c’est pourquoi l’appel système à "supprimer" un fichier s’appelle 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.

La seule façon 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.

59
Laurence Gonsalves

UNIX a des liens physiques et des liens symboliques (créés respectivement avec "ln" et "ln -s"). 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 les données sous-jacentes exact mêmes. Les données d'un fichier sont spécifiées par sa 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 physiques 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 libérée que lorsque tous les fichiers utilisant cet inode sont supprimés (et tous les processus le ferment également, mais le problème est différent).

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

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
33
user53528
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
24
eyelidlessness

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 souhaitez connaître tous ses liens physiques disséminés dans le répertoire <TARGETDIR> (qui peut même correspondre au système de fichiers complet désigné par /)

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

Étendre la logique si vous voulez connaître tous les fichiers du <SOURCEDIR> ayant 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 {} \; 
10
Loves Probability

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 le système de fichiers entier à la recherche de -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 d'avoir modifié ma commande d'origine pour prendre en charge un FS-id (%D) et pour gérer tous les types de fichiers autres que des répertoires, pas uniquement les fichiers normaux. Cela permettra de retrouver vos liens symboliques, vos canaux, etc. multipliés.

Utiliser ! -type d -links +1 signifie que l'entrée de cette sorte est seulement aussi grande que la sortie finale de uniq. Sauf si vous l'exécutez dans un sous-répertoire contenant uniquement l'un des liens physiques. Quoi qu'il en soit, cela nécessitera 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 ?: un-pad la sortie. uniq a 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 la sortie les mélange).

Un sort -k 3 final trierait 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 en utilisant GNU sort --zero-terminated -k 3 pourrait faire l'affaire. tr ne fonctionne que sur des caractères simples, pas 2- - 1 ou 1-> 2 motifs, cependant. Perl le ferait (ou simplement analyser et trier dans Perl ou awk). sed pourrait aussi fonctionner.

4
Peter Cordes

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 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 semblable que possible au vôtre pour faciliter la comparaison.

Commentaires sur ce script et le vôtre

  • Il faut toujours éviter la magie $IFS 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).

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

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

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

  • GNU find est puissant.

  • Vos invocations head et tail auraient pu être traitées directement dans awk avec par ex. la commande exit et/ou en sélectionnant la variable NR. 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 grep.

3
Daniel Andersson

Basé sur le script findhardlinks (renommé en hard-links), voici 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 "";
2
Torocoro-Macho

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é de précédents commentateurs, les fichiers "noms" ne sont que de simples alias pour les mêmes données. Cependant, il existe 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 ..." dans 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 les recherches du programme.

1
Charles

Vous pouvez configurer ls pour mettre en surbrillance les liens durs à l'aide d'un 'alias', mais comme indiqué précédemment, il n'y a aucun moyen d'afficher la 'source' du lien dur, c'est pourquoi j'ajoute .hardlink pour vous aider.

 highlight hardlinks

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'
1
Daniel Sokolowski