je cours
ln /a/A /b/B
Je voudrais voir dans le dossier a
où le fichier A pointe par ls
.
je cours
ln /a/A /b/B
Je voudrais voir dans le dossier a
où le fichier A pointe par ls
.
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 l’inode NUM dans le répertoire courant (.)
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.
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
. ./findhardlinks.bash
être dans Zsh d’OS X. Ma fenêtre actuelle dans l'écran se ferme.
INUM=$(stat -c %i $1)
. Aussi NUM_LINKS=$(stat -c %h $1)
. Voir man stat
pour plus de variables de format que vous pouvez utiliser.
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
inode
contenu du disque.
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 {} \;
-type f
parce que le fichier peut être un répertoire aussi.
.
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
.
O(n^2)
et s'exécute 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
pourrait 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)
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 -samefile
recherche 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 /bar
est 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 d
refuser 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 videUtiliser ! -type d -links +1
signifie 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 awk
ou cut
. 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 que le résultat les mélange)
Une finale sort -k 3
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 l’utilisation de GNU sort --zero-terminated -k 3
pourraient faire l’essentiel. 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 perl ou awk). sed
pourrait aussi fonctionner.
%D
est 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 umount
ndlr), 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_t
et ino_t
est de 64 bits de long aujourd'hui. Cela tiendra probablement aussi longtemps que nous aurons des systèmes 64 bits.
! -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.)
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.
Il faut toujours éviter la $IFS
magie 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 ls
autant que possible d' analyser manuellement ce type de sortie, car elle vous mordra tôt ou tard. Par exemple: dans votre première awk
ligne, vous échouez sur tous les noms de fichiers contenant des espaces.
printf
sera souvent sauver des problèmes à la fin car il est si robuste avec la %s
syntaxe. 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 head
et tail
auraient pu être traitées directement awk
avec par exemple la exit
commande et / ou la sélection sur la NR
variable. Cela permettrait d'économiser les invocations de processus, qui améliorent presque toujours les performances dans des scripts laborieux.
Votre egrep
s pourrait tout aussi bien être juste grep
.
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 ...
Basé sur le findhardlinks
script (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 "";
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.
Vous pouvez configurer la ls
mise 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 .hardlink
pour vous aider.
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'
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 estfind / -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ésigneunlink(2)ed
. (Ceci est le "nombre de liens" enls
sortie).