Je cours
ln /a/A /b/B
Je voudrais voir dans le dossier a
où le fichier A est pointé par ls
.
Je cours
ln /a/A /b/B
Je voudrais voir dans le dossier a
où le fichier A est pointé par ls
.
Il y a beaucoup de réponses avec scripts pour trouver tous les hardlinks dans un système de fichiers. La plupart d'entre elles font des choses stupides comme lancer find pour scanner tout le système de fichiers à la recherche de -samefile
pour CHAQUE fichier à liens multiples. C'est de la folie ; tout ce dont vous avez besoin est de trier sur le numéro d'inode et d'imprimer les doublons.
Il suffit d'un seul passage sur le système de fichiers pour trouver et regrouper tous les ensembles de fichiers liés entre eux.
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 des ensembles multiples de fichiers liés entre eux.find /foo -samefile /bar
est excellent pour un seul fichier.
-xdev
: limite à un seul système de fichiers. Pas strictement nécessaire puisque nous imprimons aussi le FS-id à uniq sur! -type d
rejeter les répertoires : le .
y ..
Les entrées signifient qu'elles sont toujours liées.-links +1
: nombre de liens strictement > 1
-printf ...
affiche l'identifiant FS, le numéro d'inode et le chemin. (Avec le remplissage à des largeurs de colonne fixes que nous pouvons dire uniq
à peu près.)sort -n | uniq ...
tri numérique et unicité sur les 42 premières colonnes, en séparant les groupes par une ligne blancheUtilisation de ! -type d -links +1
signifie que l'entrée de sort est seulement aussi grande que la sortie finale de uniq, donc nous ne faisons pas une énorme quantité de tri de chaînes. A moins que vous ne l'exécutiez sur un sous-répertoire qui ne contient qu'un seul d'un ensemble de liens durs. Quoi qu'il en soit, cela utilisera BEAUCOUP moins de temps CPU pour retraverser le système de fichiers que toute autre solution affiché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épaqueter la sortie avec awk
o cut
. uniq
a un support très limité pour la sélection des champs, donc je rembourre la sortie de find et j'utilise une largeur fixe. La largeur de 20 caractères est suffisante pour le numéro maximal possible d'inode ou de périphérique (2^64-1 = 18446744073709551615). XFS choisit les numéros d'inode en fonction de l'endroit du disque où ils sont alloués, et non de manière contiguë à partir de 0, de sorte que les grands systèmes de fichiers XFS peuvent avoir des numéros d'inode >32 bits même s'ils n'ont 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 : trier les groupes de doublons par chemin. Les trier par point de montage puis par numéro d'inode mélange les choses, si vous avez deux sous-répertoires différents qui ont beaucoup de hardlinks. (c'est-à-dire que les groupes de doublons vont ensemble, mais la sortie les mélange).
Une finale sort -k 3
permet de trier les lignes séparément, et non les groupes de lignes comme un seul enregistrement. Le prétraitement avec quelque chose pour transformer une paire de nouvelles lignes en un octet NUL, et l'utilisation de GNU sort --zero-terminated -k 3
pourrait faire l'affaire. tr
ne fonctionne que sur des caractères uniques, et non sur des motifs 2->1 ou 1->2. perl
le ferait (ou simplement analyser et trier avec perl ou awk). sed
pourrait également fonctionner.
Il s'agit en quelque sorte d'un commentaire à la réponse et au script de Torocoro-Macho, mais il ne rentre évidemment pas dans la boîte à commentaires.
Réécriture de votre script avec des moyens plus directs pour trouver l'information, et donc beaucoup moins d'invocations 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 au vôtre que possible pour faciliter la comparaison.
Il faut toujours éviter $IFS
magique si un glob suffit, puisque c'est inutilement alambiqué, et que les noms de fichiers peuvent effectivement contenir des nouvelles lignes (mais en pratique, c'est surtout la première raison).
Vous devriez éviter d'analyser manuellement ls
et ce genre de production autant que possible, car elle vous mordra tôt ou tard. Par exemple : dans votre premier awk
vous échouez sur tous les noms de fichiers contenant des espaces.
printf
permettra souvent d'éviter des problèmes en fin de compte, car il est si robuste avec les %s
la syntaxe. Il vous donne également un contrôle total sur la sortie, et est cohérent à travers todo contrairement aux systèmes echo
.
stat
peut vous faire gagner beaucoup de logique dans ce cas.
GNU find
est puissant.
Votre head
y tail
auraient pu être traitées directement dans awk
avec, par exemple, le exit
et/ou en sélectionnant sur le NR
variable. Cela permettrait d'économiser les invocations de processus, ce qui améliore presque toujours gravement les performances des scripts qui travaillent dur.
Votre egrep
pourrait tout aussi bien être juste grep
.
Vous pouvez configurer ls
pour mettre en évidence les liens durs en utilisant un 'alias', mais comme indiqué précédemment il n'y a aucun moyen de montrer la 'source' du lien dur, c'est pourquoi j'ajoute .hardlink
pour aider à cela.
Ajoutez le texte suivant quelque part dans votre .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Sur la base de la findhardlinks
script (renommé en hard-links
), voici ce que j'ai remanié pour que cela fonctionne.
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 GUI se rapproche vraiment de votre question :
Vous ne pouvez pas lister les fichiers liés en dur à partir de "ls" car, comme les commentateurs précédents l'ont souligné, les "noms" de fichiers sont de simples alias pour les mêmes données. Cependant, il existe en fait un outil GUI qui se rapproche vraiment de ce que vous voulez, c'est-à-dire afficher une liste des chemins d'accès aux noms de fichiers qui pointent vers les mêmes données (en tant que hardlinks) sous linux, il s'appelle FSLint. L'option que vous voulez est sous "Name clashes" -> désélectionnez "checkbox $PATH" dans Search (XX) -> et sélectionnez "Alias" dans la liste déroulante après "for..." vers le haut-milieu.
FSLint est très peu documenté mais j'ai trouvé qu'en s'assurant que l'arbre de répertoire limité sous "Search path" avec la case à cocher sélectionnée pour "Recurse ?" et les options susmentionnées, une liste de données liées avec des chemins et des noms qui "pointent" vers les mêmes données est produite après que le programme ait effectué des recherches.
SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.