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 (.)
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.
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
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
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 {} \;
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.
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.
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 egrep
s pourrait tout aussi bien être grep
.
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 "";
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.
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.
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'