Hoe kun je de echte harde link zien door ls?
Ik draai
ln /a/A /b/B
Ik zou graag in de map a
willen zien waar het bestand A naar toe wijst door ls
.
Ik draai
ln /a/A /b/B
Ik zou graag in de map a
willen zien waar het bestand A naar toe wijst door ls
.
U kunt vinden inode nummer voor uw bestand met
ls -i
en
ls -l
toont verwijzingen tellen (aantal hardlinks naar een bepaalde inode)
nadat u gevonden inode nummer, kunt u zoeken naar alle bestanden met dezelfde inode:
find . -inum NUM
toont bestandsnamen voor inode NUM in huidige dir (.)
Er is niet echt een duidelijk antwoord op je vraag. In tegenstelling tot symlinks, zijn hardlinks niet te onderscheiden van het “originele bestand”.
Directory entries bestaan uit een bestandsnaam en een pointer naar een inode. De inode bevat op zijn beurt de metadata van het bestand en (pointers naar) de eigenlijke bestandsinhoud). Het maken van een hard link creëert een andere bestandsnaam + verwijzing naar dezelfde inode. Deze referenties zijn unidirectioneel (in typische bestandssystemen, tenminste) – de inode houdt alleen een referentietelling bij. Er is geen intrinsieke manier om uit te vinden wat de “originele” bestandsnaam is.
Tussen haakjes, dit is waarom de system call om een bestand te “verwijderen” unlink
heet. Het verwijdert alleen een hardlink. De inode en de bijbehorende gegevens worden alleen verwijderd als het aantal verwijzingen naar de inode tot 0 is gedaald.
De enige manier om de andere verwijzingen naar een gegeven inode te vinden is door het bestandssysteem uitputtend te doorzoeken om te zien welke bestanden naar de inode in kwestie verwijzen. U kunt ‘test A -ef B’ vanuit de shell gebruiken om deze controle uit te voeren.
ls -l
De eerste kolom geeft de permissies weer. De tweede kolom is het aantal sub-items (voor directories) of het aantal paden naar dezelfde gegevens (hard links, inclusief het originele bestand) naar het bestand. Bijv:
-rw-r--r--@ 2 [username] [group] [timestamp] HardLink
-rw-r--r--@ 2 [username] [group] [timestamp] Original
^ Number of hard links to the data
Wat dacht je van de volgende eenvoudigere? (De laatste zou de lange scripts hierboven kunnen vervangen!)
Als je een specifiek bestand hebt <THEFILENAME>
en je wilt alle hardlinks daarvan weten verspreid over de directory <TARGETDIR>
, (die zelfs het hele bestandssysteem kan zijn dat wordt aangeduid met /
)
find <TARGETDIR> -type f -samefile <THEFILENAME>
De logica uitbreiden, als je alle bestanden wilt weten in de <SOURCEDIR>
met meerdere hardlinks verspreid over <TARGETDIR>
:
find <SOURCEDIR> -type f -links +1 \
-printf "\n\n %n HardLinks of file : %H/%f \n" \
-exec find <TARGETDIR> -type f -samefile {} \;
Er zijn veel antwoorden met scripts om alle hardlinks in een bestandssysteem te vinden. De meeste van hen doen domme dingen zoals het uitvoeren van find om het hele bestandssysteem te scannen op -samefile
voor ELK meervoudig gelinkt bestand. Dit is gekkenwerk; je hoeft alleen maar op inode nummer te sorteren en duplicaten te printen.
Met slechts één pass over het bestandssysteem om alle sets van hard-gekoppelde bestanden te vinden en te groeperen
find dirs -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
sort -n | uniq -w 42 --all-repeated=separate
Dit is veel sneller dan de andere antwoorden voor het vinden van meerdere sets van hard-gekoppelde bestanden.
find /foo -samefile /bar
is uitstekend voor slechts één bestand.
-xdev
: beperken tot één bestandssysteem. Niet strikt noodzakelijk omdat we ook de FS-id naar uniq printen op ! -type d
afgewezen directories: de .
en ..
entries betekenen dat ze altijd gelinkt zijn. -links +1
: link count strikt > 1
-printf ...
FS-id, inode nummer, en pad afdrukken. (Met padding tot vaste kolombreedtes waarover we uniq
kunnen vertellen.) sort -n | uniq ...
numeriek sorteren en uniquify op de eerste 42 kolommen, groepen scheidend met een lege regel ! -type d -links +1
gebruiken betekent dat de invoer van sort slechts zo groot is als de uiteindelijke uitvoer van uniq, zodat we niet een enorme hoeveelheid string-sortering doen. Tenzij je het uitvoert op een subdirectory die slechts één van een set hardlinks bevat. Hoe dan ook, dit zal VEEL minder CPU-tijd gebruiken om het bestandssysteem opnieuw te doorlopen dan elke andere geposte oplossing.
voorbeeld uitvoer:
...
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?: maak de uitvoer on-pad met awk
of cut
. uniq
heeft zeer beperkte veld-selectie ondersteuning, dus ik pad de find output en gebruik fixed-width. 20 karakters is breed genoeg voor het maximaal mogelijke inode- of apparaatnummer (2^64-1 = 18446744073709551615). XFS kiest inode-nummers gebaseerd op waar op de schijf ze worden toegewezen, niet contigu vanaf 0, dus grote XFS bestandssystemen kunnen inode-nummers van >32bit hebben, zelfs als ze geen miljarden bestanden hebben. Andere bestandssystemen kunnen 20-cijferige inode nummers hebben, zelfs als ze niet gigantisch zijn.
TODO: sorteer groepen van duplicaten op pad. Door ze te sorteren op mount-punt en dan op inode-nummer worden dingen door elkaar gehaald, als je een paar verschillende subdirs hebt die veel hardlinks hebben. (d.w.z. groepen dup-groepen gaan samen, maar de uitvoer haalt ze door elkaar).
Een laatste sort -k 3
zou regels apart sorteren, niet groepen van regels als een enkel record. Voorbewerken met iets om een paar nieuwe regels om te zetten in een NUL byte, en GNU sort --zero-terminated -k 3
gebruiken zou kunnen werken. tr
werkt echter alleen op enkele karakters, niet op 2-[1] of 1-[2] patronen. perl
zou het kunnen doen (of gewoon parsen en sorteren in perl of awk). sed
zou ook kunnen werken.
Dit is een beetje een commentaar op Torocoro-Macho’s eigen antwoord en script, maar het past natuurlijk niet in het commentaarvak.
Herschreef je script met meer eenvoudige manieren om de info te vinden, en dus een stuk minder procesaanroepen.
#!/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
Ik heb geprobeerd het zo veel mogelijk op dat van jou te laten lijken om het makkelijk te kunnen vergelijken.
Je moet altijd de $IFS
magie vermijden als een glob volstaat, omdat het onnodig ingewikkeld is, en bestandsnamen eigenlijk newlines kunnen bevatten (maar in de praktijk meestal de eerste reden).
Je moet het handmatig parsen van ls
en dergelijke uitvoer zo veel mogelijk vermijden, omdat het je vroeg of laat zal bijten. Bijvoorbeeld: in je eerste awk
regel, faal je op alle bestandsnamen die spaties bevatten.
printf
zal uiteindelijk vaak problemen besparen, omdat het zo robuust is met de %s
syntax. Het geeft je ook volledige controle over de uitvoer, en is consistent over alle systemen, in tegenstelling tot echo
.
stat
kan je in dit geval een hoop logica besparen.
GNU find
is krachtig.
Je head
en tail
aanroepingen hadden direct in awk
afgehandeld kunnen worden met b.v. het exit
commando en/of het selecteren op de NR
variabele. Dit zou proces aanroepen besparen, wat bijna altijd de performance sterk verbetert in hard-werkende scripts.
Je egrep
s zouden net zo goed gewoon grep
kunnen zijn.
Gebaseerd op het findhardlinks
script (hernoemd naar hard-links
), is dit wat ik heb gerefactored en het werkend heb gemaakt.
Uitgang:
# ./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 "";
Een GUI oplossing komt heel dicht bij je vraag:
Je kunt de eigenlijke hardlinked bestanden van “ls” niet opnoemen omdat, zoals eerdere commentatoren al aangaven, de bestands “namen” slechts aliassen zijn naar dezelfde gegevens. Er is echter een GUI programma dat heel dicht in de buurt komt van wat u wilt, namelijk een padlijst weergeven van bestandsnamen die naar dezelfde gegevens wijzen (als hardlinks) onder linux, het heet FSLint. De optie die u zoekt staat onder “Name clashes” -> deselecteer “checkbox $PATH” in Search (XX) -> en selecteer “Aliases” uit de drop-down box na “for…” in het midden bovenaan.
FSLint is zeer slecht gedocumenteerd, maar ik heb ontdekt dat als je ervoor zorgt dat de beperkte mapstructuur onder “Zoek pad” met het selectievakje geselecteerd voor “Recurse?” en de eerder genoemde opties, een lijst van hardlinked gegevens met paden en namen die “wijzen” naar dezelfde gegevens worden geproduceerd nadat het programma zoekt.
Je kunt ls
instellen om hardlinks te markeren met een ‘alias’, maar zoals eerder gezegd is er geen manier om de ‘bron’ van de hardlink te tonen, daarom voeg ik .hardlink
toe om daarbij te helpen.
Voeg het volgende toe ergens in je .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'