Jak można zobaczyć rzeczywisty twardy link przez ls?
Uruchamiam
ln /a/A /b/B
Chciałbym zobaczyć w folderze a
na jaki plik A wskazuje ls
.
Uruchamiam
ln /a/A /b/B
Chciałbym zobaczyć w folderze a
na jaki plik A wskazuje ls
.
Możesz znaleźć numer inode dla swojego pliku za pomocą
ls -i
a
ls -l
pokaże liczbę referencji (liczbę hardlinków do danego inode)
po znalezieniu numeru inode, możesz wyszukać wszystkie pliki z tym samym inode:
find . -inum NUM
pokaże nazwy plików dla inode NUM w aktualnym dir (.)
Tak naprawdę nie ma dobrze zdefiniowanej odpowiedzi na twoje pytanie. W przeciwieństwie do symlinków, hardlinki są nie do odróżnienia od “oryginalnego pliku”.
Wpisy katalogowe składają się z nazwy pliku i wskaźnika do węzła inode. Inode z kolei zawiera metadane pliku i (wskaźniki do) rzeczywistej zawartości pliku). Utworzenie twardego dowiązania tworzy kolejną nazwę pliku + odniesienie do tego samego inode. Odniesienia te są jednokierunkowe (przynajmniej w typowych systemach plików) – inode przechowuje tylko liczbę odniesień. Nie ma wewnętrznego sposobu, aby dowiedzieć się, która jest “oryginalna” nazwa pliku.
Przy okazji, to właśnie dlatego wywołanie systemowe do “usunięcia” pliku nazywa się unlink
. To tylko usuwa hardlink. Inode i dołączone do niego dane są usuwane tylko wtedy, gdy liczba odniesień do inode'a spadnie do 0.
Jedynym sposobem na znalezienie innych odniesień do danego inode'a jest wyczerpujące przeszukanie systemu plików sprawdzając, które pliki odnoszą się do danego inode'a. Możesz użyć ‘test A -ef B’ z powłoki, aby wykonać to sprawdzenie.
ls -l
Pierwsza kolumna będzie reprezentować uprawnienia. Druga kolumna będzie liczbą podpozycji (dla katalogów) lub liczbą ścieżek do tych samych danych (twarde linki, w tym oryginalny plik) do pliku. Na przykład:
-rw-r--r--@ 2 [username] [group] [timestamp] HardLink
-rw-r--r--@ 2 [username] [group] [timestamp] Original
^ Number of hard links to the data
Co powiesz na poniższy, prostszy sposób? (Ten ostatni może zastąpić długie skrypty powyżej!)
Jeśli masz konkretny plik <THEFILENAME>
i chcesz znać wszystkie jego hardlinki rozrzucone po katalogu <TARGETDIR>
, (który może być nawet całym systemem plików oznaczonym przez /
)
find <TARGETDIR> -type f -samefile <THEFILENAME>
Rozszerzając logikę, jeśli chcesz znać wszystkie pliki w <SOURCEDIR>
mające wiele hardlinków rozrzuconych po <TARGETDIR>
:
find <SOURCEDIR> -type f -links +1 \
-printf "\n\n %n HardLinks of file : %H/%f \n" \
-exec find <TARGETDIR> -type f -samefile {} \;
```.
Istnieje wiele odpowiedzi ze skryptami do znalezienia wszystkich hardlinków w systemie plików. Większość z nich robi głupie rzeczy, takie jak uruchamianie find, aby przeskanować cały system plików w poszukiwaniu -samefile
dla KAŻDEGO pliku z wieloma powiązaniami. To jest szalone; wszystko czego potrzebujesz to sortowanie po numerze inode i drukowanie duplikatów.
Tylko jedno przejście przez system plików, aby znaleźć i pogrupować wszystkie zestawy plików z twardymi powiązaniami
find dirs -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
sort -n | uniq -w 42 --all-repeated=separate
Jest to znacznie szybsze niż inne odpowiedzi dla znalezienia wielu zestawów plików z twardymi powiązaniami.
find /foo -samefile /bar
jest doskonały dla tylko jednego pliku.
-xdev
: ograniczenie do jednego systemu plików. Nie jest to konieczne, ponieważ wypisujemy również FS-id do uniq na ! -type d
odrzuć katalogi: wpisy .
i ..
oznaczają, że są zawsze połączone. -links +1
: licznik linków ściśle > 1
-printf ...
drukuj FS-id, numer inode i ścieżkę. (Z wypełnieniem do stałych szerokości kolumn, o których możemy powiedzieć uniq
). sort -n | uniq ...
sortowanie numeryczne i uniquify na pierwszych 42 kolumnach, oddzielając grupy pustą linią Użycie ! -type d -links +1
oznacza, że wejście sort jest tylko tak duże, jak końcowe wyjście uniq, więc nie wykonujemy dużej ilości sortowania łańcuchów. Chyba że uruchomisz go na podkatalogu, który zawiera tylko jeden z zestawu hardlinków. Tak czy inaczej, będzie to zużywać o wiele mniej czasu procesora na ponowne przemierzanie systemu plików niż jakiekolwiek inne opublikowane rozwiązanie.
przykładowe wyjście:
...
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?: rozpakuj wyjście za pomocą awk
lub cut
. uniq
ma bardzo ograniczoną obsługę wybierania pól, więc wyścielam wyjście find i używam stałej szerokości. 20 znaków jest wystarczająco szerokie dla maksymalnego możliwego numeru inode lub urządzenia (2^64-1 = 18446744073709551615). XFS wybiera numery inode na podstawie tego gdzie na dysku są alokowane, nie przylegająco od 0, więc duże systemy plików XFS mogą mieć 32-bitowe numery inode, nawet jeśli nie mają miliardów plików. Inne systemy plików mogą mieć 20-cyfrowe numery inode, nawet jeśli nie są gigantyczne.
TODO: posortuj grupy duplikatów według ścieżki. Sortowanie ich według punktu montowania, a następnie numeru inode powoduje mieszanie rzeczy razem, jeśli masz kilka różnych podkatalogów, które mają wiele hardlinków. (tj. grupy dup-grup idą razem, ale wyjście je miesza).
A końcowe sort -k 3
posortowałoby linie oddzielnie, a nie grupy linii jako pojedynczy rekord. Wstępne przetworzenie czegoś, co przekształci parę nowych linii w bajt NUL, i użycie GNU sort --zero-terminated -k 3
może załatwić sprawę. tr
działa jednak tylko na pojedynczych znakach, nie na wzorcach 2- lub 1- 2. perl
zrobi to (lub po prostu parsuje i sortuje w perlu lub awk). sed
może również zadziałać.
To jest w pewnym sensie komentarz do własnej odpowiedzi i skryptu Torocoro-Macho, ale oczywiście nie zmieści się w polu komentarza.
Przepisałem twój skrypt z bardziej prostymi sposobami na znalezienie informacji, a tym samym dużo mniej wywołań procesów.
#!/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
Starałem się zachować go tak podobnym do twojego, jak to tylko możliwe dla łatwego porównania.
Zawsze należy unikać magii $IFS
jeśli wystarczy glob, ponieważ jest to niepotrzebnie zagmatwane, a nazwy plików mogą zawierać nowe linie (ale w praktyce głównie z pierwszego powodu).
Powinieneś unikać ręcznego parsowania ls
i tego typu danych wyjściowych tak bardzo jak to tylko możliwe, ponieważ prędzej czy później cię to ugryzie. Na przykład: w twojej pierwszej linii awk
zawodzisz na wszystkich nazwach plików zawierających spacje.
printf
często zaoszczędzi kłopotów, ponieważ jest tak solidny ze składnią %s
. Daje też pełną kontrolę nad wyjściem i jest spójny we wszystkich systemach, w przeciwieństwie do echo
.
stat
może zaoszczędzić ci dużo logiki w tym przypadku.
GNU find
jest potężny.
Twoje inwokacje head
i tail
mogły być obsługiwane bezpośrednio w awk
za pomocą np. polecenia exit
i/lub wyboru na zmiennej NR
. Zaoszczędziłoby to wywołań procesów, co prawie zawsze poważnie poprawia wydajność w ciężko pracujących skryptach.
Twoje egrep
mogą być równie dobrze po prostu grep
.
Bazując na skrypcie findhardlinks
(zmieniłem jego nazwę na hard-links
), oto co udało mi się przerobić i sprawić, że działa.
Wyjście:
# ./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 "";
```.
Rozwiązanie GUI naprawdę zbliża się do twojego pytania:
Nie można wyświetlić listy rzeczywistych plików z hardlinkami z “ls”, ponieważ, jak zauważyli poprzedni komentatorzy, “nazwy” plików są jedynie aliasami do tych samych danych. Jednakże, istnieje narzędzie GUI, które jest naprawdę bliskie temu, czego chcesz, a mianowicie wyświetla listę nazw plików, które wskazują na te same dane (jako hardlinki) w linuxie, nazywa się FSLint. Opcja, której potrzebujesz znajduje się pod “Name clashes” - odznacz “checkbox $PATH” w Search (XX) - i wybierz “Aliases” z rozwijanego pola po “for…” w kierunku górnego środka.
FSLint jest bardzo słabo udokumentowany, ale odkryłem, że upewniając się, że ograniczone drzewo katalogów w “Search path” z zaznaczonym checkboxem dla “Recurse?” i wyżej wymienionymi opcjami, lista twardo powiązanych danych ze ścieżkami i nazwami, które “wskazują” na te same dane są produkowane po przeszukiwaniu przez program.
Można skonfigurować ls
do podświetlania hardlinków za pomocą “aliasu”, ale jak już wspomniano wcześniej, nie ma sposobu na pokazanie “źródła” hardlinka, dlatego dołączam .hardlink
, aby w tym pomóc.
Dodaj poniższe gdzieś w swoim .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'