Jak możesz zobaczyć rzeczywisty twardy link przez ls?


97

biegnę

ln /a/A /b/B

Chciałbym zobaczyć w folderze agdzie punkty złożyć do przez ls.


1
Twarde linki nie są wskaźnikami, są dowiązaniami symbolicznymi. Są to wiele nazw tego samego pliku (i-węzła). Po link(2)wywołaniu systemowym nie ma sensu, który z nich jest oryginalny, a drugi link. Dlatego, jak wskazują odpowiedzi, jedynym sposobem na znalezienie wszystkich linków jest find / -samefile /a/A. Ponieważ jeden wpis katalogu dla i-węzła nie „wie” o innych wpisach katalogu dla tego samego i-węzła. Wszystko, co robią, to ponownie liczą i-węzeł, aby można go było usunąć, gdy jest to jego nazwisko unlink(2)ed. (Jest to „liczba linków” na lswyjściu).
Peter Cordes

@PeterCordes: Czy przelicznik faktycznie jest zapisany we wpisie hardlink? To właśnie sugeruje twoje sformułowanie („Wszystko, co robią, to przeliczanie i-węzła ...”) Ale to nie miałoby sensu, gdyby linki nie wiedziały o sobie nic, ponieważ po jednym uaktualnieniu wszystkie inne musiałyby jakoś być na bieżąco. A może refcount jest przechowywany w samym i-węźle? (Wybacz mi, jeśli to głupie pytanie, uważam się za nowicjusza i wciąż się uczę).
loneboat

1
Rachunek zwrotny jest przechowywany w i-węźle, jak się ostatecznie domyśliłeś, na podstawie innych faktów. :) Wpisy w katalogu są nazywane wskaźnikami do i-węzłów. Nazywamy to „twardym łączeniem”, gdy masz wiele nazw wskazujących na ten sam i-węzeł.
Peter Cordes,

Odpowiedzi:


171

Możesz znaleźć numer i-węzła dla swojego pliku za pomocą

ls -i

i

ls -l

pokazuje liczbę referencji (liczba dowiązań twardych do konkretnego i-węzła)

po znalezieniu numeru i-węzła możesz wyszukać wszystkie pliki z tym samym i-węzłem:

find . -inum NUM

pokaże nazwy plików dla i-węzła NUM w bieżącym katalogu (.)


46
możesz po prostu uruchomić znaleźć. -samefile nazwa pliku
BeowulfNode42

1
@ BeowulfNode42 To polecenie jest świetne, ale wymaga przynajmniej udostępnionego folderu głównego tych samych plików.
Itachi

1
ta odpowiedź daje pragmatyczne „zrób to”, ale uważam, że @LaurenceGonsalves odpowiada na pytania „jak” i / lub „dlaczego”.
Trevor Boyd Smith

65

Naprawdę nie ma dobrze zdefiniowanej odpowiedzi na twoje pytanie. W przeciwieństwie do dowiązań symbolicznych, dowiązania twarde są nie do odróżnienia od „oryginalnego pliku”.

Pozycje katalogu składają się z nazwy pliku i wskaźnika do i-węzła. Z kolei i-węzeł zawiera metadane pliku i (wskazuje na) rzeczywistą zawartość pliku). Utworzenie twardego łącza tworzy kolejną nazwę pliku + odniesienie do tego samego i-węzła. Odwołania te są jednokierunkowe (przynajmniej w typowych systemach plików) - i-węzeł zachowuje tylko liczbę odwołań. Nie ma wewnętrznego sposobu, aby dowiedzieć się, która jest „oryginalną” nazwą pliku.

Nawiasem mówiąc, właśnie dlatego wywoływane jest systemowe wywołanie w celu „usunięcia” pliku unlink. Po prostu usuwa twardy link. I-węzeł dołączone dane są usuwane tylko wtedy, gdy liczba referencyjna i-węzła spadnie do 0.

Jedynym sposobem na znalezienie innych odniesień do danego i-węzła jest dokładne przeszukanie systemu plików i sprawdzenie, które pliki odnoszą się do danego i-węzła. Aby wykonać to sprawdzenie, możesz użyć „testu A –ef B” ze powłoki.


35
Oznacza to, że nie ma czegoś takiego jak twardy link do innego pliku , ponieważ oryginalny plik jest również twardym linkiem; twarde linki wskazują lokalizację na dysku .
jtbandes

12
@jtbandes: Twarde linki wskazują na i-węzeł, który wskazuje na rzeczywiste dane.
dash17291

33

UNIX ma dowiązania twarde i dowiązania symboliczne (odpowiednio wykonane "ln"i "ln -s"). Dowiązania symboliczne to po prostu plik, który zawiera rzeczywistą ścieżkę do innego pliku i może przechodzić przez systemy plików.

Twarde linki istnieją już od najwcześniejszych dni UNIX (które i tak pamiętam, i to już od dawna). Są to dwa wpisy katalogu, które odnoszą się dokładnie do tych samych podstawowych danych. Dane w pliku są określone przez jego inode. Każdy plik w systemie plików wskazuje na i-węzeł, ale nie ma wymogu, aby każdy plik wskazywał na unikalny i-węzeł - stąd pochodzą twarde łącza.

Ponieważ i-węzły są unikalne tylko dla danego systemu plików, istnieje ograniczenie, że dowiązania twarde muszą znajdować się w tym samym systemie plików (w przeciwieństwie do dowiązań symbolicznych). Zauważ, że w przeciwieństwie do dowiązań symbolicznych, nie ma pliku uprzywilejowanego - wszystkie są równe. Obszar danych zostanie zwolniony tylko wtedy, gdy wszystkie pliki korzystające z tego i-węzła zostaną usunięte (i wszystkie procesy również go zamkną, ale to inny problem).

Możesz użyć "ls -i"polecenia, aby uzyskać i-węzeł określonego pliku. Następnie możesz użyć "find <filesystemroot> -inum <inode>"polecenia, aby znaleźć wszystkie pliki w systemie plików z danym i-węzłem.

Oto skrypt, który właśnie to robi. Wywołujesz to za pomocą:

findhardlinks ~/jquery.js

i znajdzie wszystkie pliki w tym systemie plików, które są twardymi dowiązaniami do tego pliku:

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

Oto skrypt.

#!/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

@pax: Wygląda na to, że w skrypcie występuje błąd. Zaczynam od . ./findhardlinks.bashbycia w Zsh OS X. Moje bieżące okno na ekranie zostanie zamknięte.

4
@Masi Problem jest twój początkowy. (to samo co polecenie źródłowe). To powoduje, że polecenie exit 1 kończy działanie twojej powłoki. Użyj chmod a + x findhardlinks.bash, a następnie uruchom go za pomocą ./findhardlinks.bash lub użyj bash findhardlinks.bash
njsf

Proszę zobaczyć moją odpowiedź na twoją odpowiedź na stronie superuser.com/questions/12972/to-see-hardlinks-by-ls/...
Léo Léopold Hertz 준영

3
Aby to zrobić programowo, to chyba bardziej sprężysta, jeśli używasz zamiast tego: INUM=$(stat -c %i $1). Również NUM_LINKS=$(stat -c %h $1). Zobacz man statwięcej zmiennych formatu, których możesz użyć.
Joe

Jak dotąd najlepsza odpowiedź. Sława.
MariusMatutiae

24
ls -l

Pierwsza kolumna będzie reprezentować uprawnienia. Druga kolumna będzie liczbą podelementów (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

2
Pomocne w określeniu, JEŚLI dany plik ma [inne] twarde linki, ale nie GDZIE są.
mklement0

Ponadto nie ma technicznego rozróżnienia między twardym linkiem a oryginalnym plikiem. Oba są identyczne, ponieważ po prostu wskazują na to, inodeco z kolei wskazuje na zawartość dysku.
guyarad

13

Co powiesz na następującą prostszą? (Później może zastąpić powyższe długie skrypty!)

Jeśli masz konkretny plik <THEFILENAME>i chcesz poznać wszystkie jego łącza twarde rozproszone po katalogu <TARGETDIR>, (może to być nawet cały system plików oznaczony przez /)

find <TARGETDIR> -type f -samefile  <THEFILENAME>

Rozszerzając logikę, jeśli chcesz poznać wszystkie pliki, <SOURCEDIR>które mają wiele rozpiętych linków <TARGETDIR>:

find <SOURCEDIR> -type f -links +1   \
  -printf "\n\n %n HardLinks of file : %H/%f  \n"   \
  -exec find <TARGETDIR> -type f -samefile {} \; 

To dla mnie najlepsza odpowiedź! ale nie używałbym tego, -type fponieważ plik może być również katalogiem.
silvio

3
@silvio: Możesz tworzyć tylko twarde linki do plików , a nie katalogów.
mklement0

@ mklement0: Masz rację!
silvio

.I ..wpisy w katalogach są hardlinki. Możesz dowiedzieć się, ile podkatalogów znajduje się w katalogu na podstawie liczby linków .. To i tak jest dyskusyjne, ponieważ find -samefile .nadal nie wydrukuje żadnych subdir/..wyników. find(przynajmniej wersja GNU) wydaje się być zakodowana na stałe .., nawet z -noleaf.
Peter Cordes

Ponadto pomysł „znajdź wszystkie linki” jest O(n^2)i działa findraz dla każdego członka zestawu plików dowiązanych na stałe. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separatedziałałby (16 nie jest wystarczająco szeroki, aby dziesiętna reprezentacja 2 ^ 63-1, więc gdy twój system plików XFS jest wystarczająco duży, aby mieć tak wysokie liczby i-węzłów, uważaj)
Peter Cordes

5

Istnieje wiele odpowiedzi ze skryptami, aby znaleźć wszystkie dowiązania twarde w systemie plików. Większość z nich robi takie głupie rzeczy, jak uruchamianie funkcji wyszukiwania, aby skanować cały system plików w -samefileposzukiwaniu KAŻDEGO pliku z wieloma linkami. To jest szalone; wystarczy posortować według numeru i-węzła i wydrukować duplikaty.

Za pomocą jednego przejścia przez system plików można znaleźć i zgrupować wszystkie zestawy plików dowiązanych na stałe

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 na znalezienie wielu zestawów plików dowiązanych na stałe.
find /foo -samefile /barjest doskonały tylko dla jednego pliku.

  • -xdev: ograniczenie do jednego systemu plików. Nie jest to absolutnie potrzebne, ponieważ drukujemy również FS-id na uniq
  • ! -type dodrzuć katalogi: wpisy .i ..oznaczają, że zawsze są połączone.
  • -links +1 : liczba linków ściśle > 1
  • -printf ...wypisz FS-id, numer i-węzła i ścieżkę. (Z dopełnieniem do ustalonych szerokości kolumn, o których możemy powiedzieć uniq.)
  • sort -n | uniq ... sortuj numerycznie i unikaj pierwszych 42 kolumn, oddzielając grupy pustą linią

Użycie ! -type d -links +1oznacza, że ​​dane wejściowe sort są tak duże, jak końcowy wynik uniq, więc nie wykonujemy dużej ilości sortowania łańcuchów. Chyba że uruchomisz go w podkatalogu, który zawiera tylko jeden z zestawu dowiązań twardych. W każdym razie spowoduje to o wiele krótszy czas pracy procesora przez system plików niż w przypadku innych opublikowanych rozwiązań.

próbka wyjściowa:

...
            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ą awklub cut. uniqma bardzo ograniczone wsparcie wyboru pola, więc wpisuję wyniki wyszukiwania i używam stałej szerokości. 20 znaków jest wystarczająco szeroki, aby pomieścić maksymalny możliwy numer i-węzła lub urządzenia (2 ^ 64-1 = 18446744073709551615). XFS wybiera numery i-węzłów na podstawie miejsca, w którym są przydzielane na dysku, a nie od 0, więc duże systemy plików XFS mogą mieć> i-bitowe liczby i-węzłów, nawet jeśli nie mają miliardów plików. Inne systemy plików mogą mieć 20-cyfrowe numery i-węzłów, nawet jeśli nie są gigantyczne.

DO ZROBIENIA: posortuj grupy duplikatów według ścieżki. Posortowanie ich według punktu montowania, a następnie liczba i-węzłów miesza rzeczy razem, jeśli masz kilka różnych podkatalogów, które mają wiele dowiązań twardych. (tzn. grupy dup-grup idą w parze, ale wynik miesza je).

Finał sort -k 3sortowałby linie osobno, a nie grupy linii jako pojedynczy rekord. Przetwarzanie wstępne z czymś, co przekształca parę znaków nowej linii w bajt NUL, i użycie GNU sort --zero-terminated -k 3może załatwić sprawę. trdziała tylko na pojedyncze znaki, a nie na 2-> 1 lub 1-> 2 wzorce. perlzrobiłbym to (lub po prostu parsował i sortował w perl lub awk). sedmoże również działać.


1
%Dto identyfikator systemu plików (jest unikalny dla bieżącego rozruchu, gdy żadne systemy plików są umounted), więc po jest jeszcze bardziej ogólna: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. Działa to tak długo, jak długo dany katalog nie zawiera innego katalogu na poziomie systemu plików, a także sprawdza wszystko, co może być dowiązane na twardo (jak urządzenia lub softlink - tak, softlink może mieć liczbę linków większą niż 1). Należy pamiętać, że dev_ti ino_tma długość 64 bitów dzisiaj. Prawdopodobnie będzie tak długo, dopóki będziemy mieli 64-bitowe systemy.
Tino,

@Tino: świetna uwaga na temat używania za pomocą ! -type dzamiast -type f. Mam nawet pewne dowiązania symboliczne w moim systemie plików z organizacji niektórych zbiorów plików. Zaktualizowałem moją odpowiedź o ulepszoną wersję (ale najpierw umieściłem fs-id, więc sortuj co najmniej grupy według systemu plików.)
Peter Cordes

3

Jest to nieco komentarz do własnej odpowiedzi i skryptu Torocoro-Macho, ale oczywiście nie mieści się w polu komentarza.


Przepisz swój skrypt w prostszy sposób, aby znaleźć informacje, a tym samym znacznie 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ć jak najbardziej podobny do twojego, aby ułatwić porównanie.

Komentarze do tego skryptu i twojego

  • Zawsze należy unikać $IFSmagii, jeśli glob jest wystarczający, ponieważ jest niepotrzebnie zawinięty, a nazwy plików mogą faktycznie zawierać znaki nowej linii (ale w praktyce głównie pierwszy powód).

  • Powinieneś unikać ręcznego parsowania lsi takich danych wyjściowych, jak to możliwe, ponieważ prędzej czy później cię ugryzie. Na przykład: w pierwszym awkwierszu nie powiodły się wszystkie nazwy plików zawierające spacje.

  • printfczęsto oszczędza kłopotów, ponieważ jest tak odporny na %sskładnię. Daje także 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 headi tailwywołania mogły być obsługiwane bezpośrednio za awkpomocą np. exitPolecenia i / lub wyboru NRzmiennej. Pozwoliłoby to zaoszczędzić wywołania procesów, które prawie zawsze znacznie poprawiają wydajność w ciężko pracujących skryptach.

  • Twoje egreprównie dobrze mogą być sprawiedliwe grep.


xDEVICE = $ (stat -c% m „$ {xFILE}”) nie działa na wszystkich systemach (na przykład: stat (GNU coreutils) 6.12). Jeśli skrypt wyświetli „Element:?” na początku każdego wiersza, a następnie zastąp tę naruszającą linię linią bardziej podobną do oryginalnego skryptu, ale z xITEM przemianowaną na xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien

Jeśli chcesz tylko grupować twarde linki zamiast powtarzać się z każdym członkiem jako „master”, użyj find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. Jest O DUŻO szybszy, ponieważ przemierza fs tylko raz. W przypadku wielu FS na raz, numery i-węzłów muszą być poprzedzone identyfikatorem FS. Może zfind -exec stat... -printf ...
Peter Cordes

zamienił ten pomysł w odpowiedź
Peter Cordes

2

Opierając się na findhardlinksskrypcie (przemianowanym na hard-links), właśnie to zreformowałem i sprawiłem, że działa.

Wynik:

# ./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 "";

Komentarze do tego skryptu zamieściłem jako osobną odpowiedź.
Daniel Andersson,

1

Rozwiązanie GUI bardzo zbliża się do twojego pytania:

Nie można wyświetlić rzeczywistych plików dowiązań z „ls”, ponieważ, jak zauważyli poprzedni komentatorzy, „nazwy” plików są jedynie aliasami do tych samych danych. Jednak tak naprawdę istnieje narzędzie GUI, które naprawdę zbliża się do tego, czego chcesz, czyli wyświetlanie listy ścieżek nazw plików, które wskazują na te same dane (jako twarde linki) w systemie Linux, nazywa się FSLint. Opcja, którą chcesz znaleźć, znajduje się w „Zderzenia nazw” -> odznacz „pole wyboru $ ŚCIEŻKA” w Wyszukiwarce (XX) -> i wybierz „Aliasy” z rozwijanego menu po „dla ...” w kierunku górnej środkowej części.

FSLint jest bardzo słabo udokumentowany, ale stwierdziłem, że upewniłem się, że drzewo katalogu jest ograniczone pod „Ścieżką wyszukiwania” z zaznaczonym polem wyboru „Recurse?” oraz wyżej wspomniane opcje, lista danych połączonych na stałe ze ścieżkami i nazwami, które „wskazują” na te same dane, są tworzone po wyszukiwaniu programu.


FSlint można znaleźć na pixelbeat.org/fslint
mklement0

1

Możesz skonfigurować lspodświetlanie linków twardych za pomocą „aliasu”, ale jak wspomniano wcześniej, nie ma sposobu, aby pokazać „źródło” linku twardego, dlatego dołączam .hardlinkdo pomocy.

zaznacz linki twarde

Dodaj następujące gdzieś w swoim .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.