Bagaimana Anda bisa melihat tautan keras yang sebenarnya oleh ls?


97

saya berlari

ln /a/A /b/B

Saya ingin melihat folder atempat file A menunjuk ls.


1
Hard link bukan pointer, symlinks. Mereka banyak nama untuk file yang sama (inode). Setelah link(2)pemanggilan sistem, tidak ada arti di mana yang asli dan yang satu adalah tautan. Inilah sebabnya, seperti yang ditunjukkan jawabannya, satu-satunya cara untuk menemukan semua tautan adalah find / -samefile /a/A. Karena satu entri direktori untuk inode tidak "tahu tentang" entri direktori lain untuk inode yang sama. Yang mereka lakukan adalah menghitung ulang inode sehingga bisa dihapus ketika nama terakhirnya unlink(2)ed. (Ini adalah "jumlah tautan" dalam lsoutput).
Peter Cordes

@PeterCordes: Apakah refcount benar-benar disimpan DI entri hardlink? Itulah yang tersirat dari kata-kata Anda ("Yang mereka lakukan hanyalah menghitung ulang inode ...") Tapi itu tidak masuk akal jika tautan tidak tahu apa-apa tentang satu sama lain, karena ketika satu diperbarui, semua yang lain entah bagaimana harus diperbarui. Atau apakah refcount disimpan dalam inode itu sendiri? (Maafkan saya jika ini pertanyaan bodoh, saya menganggap diri saya seorang pemula dan saya masih belajar).
loneboat

1
Refcount disimpan dalam inode, karena Anda akhirnya tahu pasti demikian, dari fakta lain. :) Entri direktori diberi nama pointer ke inode. Kami menyebutnya "tautan keras" ketika Anda memiliki beberapa nama yang menunjuk ke inode yang sama.
Peter Cordes

Jawaban:


171

Anda dapat menemukan nomor inode untuk file Anda

ls -i

dan

ls -l

menunjukkan jumlah referensi (jumlah hardlink ke inode tertentu)

setelah Anda menemukan nomor inode, Anda dapat mencari semua file dengan inode yang sama:

find . -inum NUM

akan menampilkan nama file untuk inode NUM di dir saat ini (.)


46
Anda bisa menjalankan find. -samefile filename
BeowulfNode42

1
@ BeowulfNode42 Perintah ini bagus, tetapi setidaknya perlu folder root bersama dari file yang sama.
Itachi

1
jawaban ini memberikan pragmatis "lakukan ini" tetapi saya merasa sangat kuat bahwa @LaurenceGonsalves menjawab pertanyaan "bagaimana" dan / atau "mengapa".
Trevor Boyd Smith

65

Sebenarnya tidak ada jawaban yang jelas untuk pertanyaan Anda. Tidak seperti symlink, hardlink tidak dapat dibedakan dari "file asli".

Entri direktori terdiri dari nama file dan penunjuk ke inode. Inode pada gilirannya berisi metadata file dan (menunjuk ke) isi file yang sebenarnya). Membuat tautan keras membuat nama file + referensi lain ke inode yang sama. Referensi ini bersifat searah (setidaknya dalam sistem file biasa) - inode hanya menyimpan jumlah referensi. Tidak ada cara intrinsik untuk mengetahui nama file mana yang "asli".

Ngomong-ngomong, inilah mengapa system call untuk "menghapus" file dipanggil unlink. Itu hanya menghapus hardlink. Inode data yang dilampirkan dihapus hanya jika jumlah referensi inode turun menjadi 0.

Satu-satunya cara untuk menemukan referensi lain ke inode yang diberikan adalah mencari sistem file secara menyeluruh memeriksa file mana yang merujuk ke inode yang dimaksud. Anda dapat menggunakan 'test A -ef B' dari shell untuk melakukan pemeriksaan ini.


35
Itu berarti bahwa tidak ada yang namanya tautan keras ke file lain , karena file aslinya juga merupakan tautan keras; tautan keras menunjuk ke suatu lokasi pada disk .
jtbandes

12
@jtbandes: Tautan keras menunjuk ke inode yang menunjuk ke data aktual.
dash17291

33

UNIX memiliki tautan keras dan tautan simbolik (dibuat dengan "ln"dan "ln -s"masing - masing). Tautan simbolik hanyalah sebuah file yang berisi jalur asli ke file lain dan dapat melintasi sistem file.

Tautan keras telah ada sejak masa-masa awal UNIX (yang dapat saya ingat pula, dan itu akan kembali cukup lama). Mereka adalah dua entri direktori yang mereferensikan data dasar yang sama persis . Data dalam file ditentukan oleh inode. Setiap file pada sistem file menunjuk ke inode tetapi tidak ada persyaratan bahwa setiap file menunjuk ke inode yang unik - dari situlah hard link berasal.

Karena inode hanya unik untuk sistem file yang diberikan, ada batasan bahwa tautan keras harus berada di sistem file yang sama (tidak seperti tautan simbolik). Perhatikan bahwa, tidak seperti tautan simbolis, tidak ada file istimewa - semuanya sama. Area data hanya akan dirilis ketika semua file yang menggunakan inode itu dihapus (dan semua proses menutupnya juga, tapi itu masalah yang berbeda).

Anda dapat menggunakan "ls -i"perintah untuk mendapatkan inode dari file tertentu. Anda kemudian dapat menggunakan "find <filesystemroot> -inum <inode>"perintah untuk menemukan semua file di sistem file dengan inode yang diberikan.

Inilah skrip yang melakukan hal itu. Anda memohonnya dengan:

findhardlinks ~/jquery.js

dan ia akan menemukan semua file pada sistem file itu yang merupakan tautan keras untuk file itu:

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

Ini skripnya.

#!/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: Sepertinya ada bug dalam skrip. Saya memulainya dengan . ./findhardlinks.bashberada di OS X Zsh. Jendela saya saat ini di Layar ditutup.

4
@Masi Masalahnya adalah inisial Anda. (sama dengan perintah sumber). Itu menyebabkan perintah exit 1 untuk keluar dari shell Anda. Gunakan chmod a + x findhardlinks.bash kemudian jalankan dengan ./findhardlinks.bash atau gunakan bash findhardlinks.bash
njsf


3
Untuk melakukan hal ini pemrograman, itu mungkin lebih tangguh jika Anda menggunakan ini sebagai gantinya: INUM=$(stat -c %i $1). Juga NUM_LINKS=$(stat -c %h $1). Lihat man statuntuk variabel format lainnya yang dapat Anda gunakan.
Joe

Jawaban terbaik, sejauh ini. Pujian.
MariusMatutiae

24
ls -l

Kolom pertama akan mewakili izin. Kolom kedua adalah jumlah sub-item (untuk direktori) atau jumlah jalur ke data yang sama (tautan keras, termasuk file asli) ke file. Misalnya:

-rw-r--r--@    2    [username]    [group]    [timestamp]     HardLink
-rw-r--r--@    2    [username]    [group]    [timestamp]     Original
               ^ Number of hard links to the data

2
Membantu menentukan JIKA file yang diberikan memiliki tautan keras [lain], tetapi tidak DI MANA mereka.
mklement0

Juga, tidak ada perbedaan teknis antara hard-link dan file asli. Keduanya identik karena mereka hanya menunjuk ke inodemana pada gilirannya menunjuk ke konten disk.
guyarad

13

Bagaimana dengan yang lebih sederhana berikut ini? (Belakangan mungkin mengganti skrip panjang di atas!)

Jika Anda memiliki file tertentu <THEFILENAME>dan ingin tahu semua hardlink-nya tersebar di direktori <TARGETDIR>, (yang bahkan bisa menjadi seluruh sistem file yang ditandai oleh /)

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

Memperluas logika, jika Anda ingin mengetahui semua file dalam <SOURCEDIR>memiliki banyak tautan keras tersebar di <TARGETDIR>:

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

Bagi saya ini jawaban terbaik! tetapi saya tidak akan menggunakan -type fkarena file juga dapat menjadi direktori.
silvio

3
@ silvio: Anda hanya dapat membuat tautan keras ke file , bukan direktori.
mklement0

@ mklement0: Anda benar!
silvio

The .dan ..entri dalam direktori adalah hardlinks. Anda dapat mengetahui berapa banyak subdir dalam direktori dari jumlah tautan .. Ini masih bisa diperdebatkan, karena find -samefile .masih tidak akan mencetak subdir/..output apa pun . find(setidaknya versi GNU) tampaknya hardcoded untuk diabaikan .., bahkan dengan -noleaf.
Peter Cordes

juga, ide temukan-semua-tautan itu O(n^2), dan berjalan findsekali untuk setiap anggota dari sekumpulan file yang di-hardlink. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separateakan bekerja, (16 tidak cukup lebar untuk representasi desimal 2 ^ 63-1, jadi ketika sistem file XFS Anda cukup besar untuk memiliki nomor inode yang tinggi, hati-hati)
Peter Cordes

5

Ada banyak jawaban dengan skrip untuk menemukan semua hardlink di sistem file. Sebagian besar dari mereka melakukan hal-hal konyol seperti menjalankan find untuk memindai seluruh sistem file -samefileuntuk setiap file yang terhubung multipel. Ini gila; yang Anda butuhkan hanyalah mengurutkan pada nomor inode dan mencetak duplikat.

Dengan hanya satu melewati filesystem untuk menemukan dan mengelompokkan semua set file yang di-link

find dirs   -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
    sort -n | uniq -w 42 --all-repeated=separate

Ini jauh lebih cepat daripada jawaban lain untuk menemukan beberapa set file yang di-hardlink.
find /foo -samefile /barsangat bagus untuk hanya satu file.

  • -xdev: batasi ke satu sistem file. Tidak sepenuhnya diperlukan karena kami juga mencetak FS-id untuk di-uniq
  • ! -type dtolak direktori: entri .dan ..artinya mereka selalu ditautkan.
  • -links +1 : penghitungan tautan dengan ketat > 1
  • -printf ...cetak FS-id, nomor inode, dan path. (Dengan padding untuk memperbaiki lebar kolom yang bisa kami ceritakan uniq.)
  • sort -n | uniq ... pengurutan numerik dan uniquify pada 42 kolom pertama, pisahkan grup dengan garis kosong

Menggunakan ! -type d -links +1berarti input sortir hanya sebesar output akhir dari uniq jadi kami tidak melakukan sejumlah besar penyortiran string. Kecuali Anda menjalankannya pada subdirektori yang hanya berisi satu dari sekumpulan hardlink. Bagaimanapun, ini akan menggunakan BANYAK waktu CPU yang lebih sedikit untuk melintasi kembali sistem file daripada solusi yang diposting lainnya.

output sampel:

...
            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 ?: batalkan keluaran dengan awkatau cut. uniqmemiliki dukungan pemilihan bidang yang sangat terbatas, jadi saya mencari hasil keluaran dan menggunakan lebar tetap. 20chars cukup lebar untuk inode atau nomor perangkat maksimum yang dimungkinkan (2 ^ 64-1 = 18446744073709551615). XFS memilih nomor inode berdasarkan di mana pada disk mereka dialokasikan, tidak bersebelahan dari 0, sehingga sistem file XFS besar dapat memiliki nomor inode> 32bit bahkan jika mereka tidak memiliki miliaran file. Sistem file lain mungkin memiliki nomor inode 20 digit bahkan jika mereka tidak raksasa.

TODO: mengurutkan grup duplikat berdasarkan jalur. Setelah mereka diurutkan berdasarkan mount point kemudian nomor inode mencampur semuanya, jika Anda memiliki beberapa subdir yang berbeda yang memiliki banyak hardlink. (Yaitu grup dup-kelompok berjalan bersama, tetapi output mencampurnya).

Final sort -k 3akan mengurutkan garis secara terpisah, bukan kelompok garis sebagai rekaman tunggal. Memproses ulang dengan sesuatu untuk mengubah sepasang baris baru ke byte NUL, dan menggunakan GNU sort --zero-terminated -k 3mungkin melakukan trik. trhanya beroperasi pada karakter tunggal, bukan 2-> 1 atau 1-> 2 pola. perlakan melakukannya (atau hanya menguraikan dan mengurutkan dalam perl atau awk). sedmungkin juga berfungsi.


1
%Dadalah filesystem identifier (itu adalah unik untuk boot saat ini sementara tidak ada filesystem yang umounted), sehingga berikut ini bahkan lebih generik: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. Ini berfungsi selama tidak ada direktori yang berisi direktori lain pada tingkat filesystem, juga terlihat pada semua yang dapat di-hardlink (seperti perangkat atau softlink - ya, softlink dapat memiliki jumlah tautan lebih dari 1). Catat itu dev_tdan ino_tpanjangnya 64 bit hari ini. Ini kemungkinan akan bertahan selama kita memiliki sistem 64 bit.
Tino

@Tino: titik bagus tentang menggunakan ! -type d, bukan -type f. Saya bahkan memiliki beberapa symlink hardlink pada sistem file saya dari mengatur beberapa koleksi file. Memperbarui jawaban saya dengan versi perbaikan Anda (tapi saya menempatkan fs-id pertama, jadi urutan setidaknya kelompok berdasarkan sistem file.)
Peter Cordes

3

Ini agak komentar untuk jawaban dan skrip Torocoro-Macho sendiri, tetapi jelas tidak cocok di kotak komentar.


Menulis ulang skrip Anda dengan cara yang lebih mudah untuk menemukan info, dan dengan demikian proses pemanggilan jauh lebih sedikit.

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

Saya mencoba menjaganya agar tetap serupa dengan milik Anda agar mudah untuk perbandingan.

Komentar pada skrip ini dan milik Anda

  • Orang harus selalu menghindari $IFSkeajaiban jika gumpalan cukup, karena tidak perlu berbelit-belit, dan nama file sebenarnya dapat berisi baris baru (tetapi dalam praktiknya sebagian besar alasan pertama).

  • Anda harus menghindari penguraian secara manual lsdan output sebanyak mungkin, karena akan cepat atau lambat akan menggigit Anda. Misalnya: di awkbaris pertama Anda, Anda gagal pada semua nama file yang berisi spasi.

  • printfakan sering menyimpan masalah pada akhirnya karena sangat kuat dengan %ssintaksis. Ini juga memberi Anda kontrol penuh atas output, dan konsisten di semua sistem, tidak seperti echo.

  • stat dapat menghemat banyak logika dalam hal ini.

  • GNU find sangat kuat.

  • Anda headdan taildoa bisa ditangani secara langsung awkdengan misalnya exitperintah dan / atau memilih NRvariabel. Ini akan menghemat pemanggilan proses, yang hampir selalu menyaingi kinerja yang parah dalam skrip yang bekerja keras.

  • egrepS Anda bisa saja adil grep.


xDEVICE = $ (stat -c% m "$ {xFILE}") tidak berfungsi di semua sistem (misalnya: stat (GNU coreutils) 6.12). Jika skrip menampilkan "Item:?" di depan setiap baris, lalu ganti baris yang menyinggung ini dengan garis yang lebih mirip dengan skrip asli, tetapi dengan xITEM diganti nama menjadi xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien

Jika Anda hanya ingin grup hardlink, daripada diulangi dengan masing-masing anggota sebagai "master", gunakan find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. Ini JAUH lebih cepat, karena hanya melintasi fs sekali. Untuk beberapa FS sekaligus, Anda harus awalan nomor inode dengan id FS. Mungkin denganfind -exec stat... -printf ...
Peter Cordes

mengubah ide itu menjadi jawaban
Peter Cordes

2

Berdasarkan findhardlinksskrip (diubah namanya menjadi hard-links), inilah yang telah saya refactored dan membuatnya berfungsi.

Keluaran:

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

Saya mengirim komentar pada skrip ini sebagai jawaban terpisah.
Daniel Andersson

1

Solusi GUI sangat dekat dengan pertanyaan Anda:

Anda tidak dapat membuat daftar file yang di-hardlink dari "ls" karena, seperti yang ditunjukkan oleh komentator sebelumnya, file "nama" hanyalah alias untuk data yang sama. Namun, sebenarnya ada alat GUI yang mendekati apa yang Anda inginkan yaitu untuk menampilkan daftar path nama file yang mengarah ke data yang sama (seperti hardlink) di linux, itu disebut FSLint. Opsi yang Anda inginkan ada di bawah "Nama bentrokan" -> hapus centang "kotak centang $ PATH" di Cari (XX) -> dan pilih "Alias" dari kotak drop-down setelah "untuk ..." menuju ke tengah-atas.

FSLint didokumentasikan dengan sangat buruk tetapi saya menemukan bahwa memastikan pohon direktori terbatas di bawah "Jalur pencarian" dengan kotak centang yang dipilih untuk "Recurse?" dan opsi-opsi yang disebutkan di atas, daftar data yang di-hardlink dengan jalur dan nama yang "menunjuk" ke data yang sama dihasilkan setelah pencarian program.


Fslint dapat ditemukan di pixelbeat.org/fslint
mklement0

1

Anda dapat mengonfigurasi lsuntuk menyoroti hardlink menggunakan 'alias', tetapi seperti yang dinyatakan sebelumnya tidak ada cara untuk menunjukkan 'sumber' dari hardlink itu sebabnya saya menambahkan .hardlinkuntuk membantu dengan itu.

sorot hardlink

Tambahkan berikut ini di suatu tempat di Anda .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.