Apakah dua file di-hardlink?

27

Bagaimana saya bisa tahu jika dua file terhubung dengan keras dari baris perintah? mis. tautan sesuatu ini:

$ ls
fileA fileB fileC

$ is-hardlinked fileA fileB
yes

$ is-hardlinked fileA fileC
no
Vincent Scheib
sumber

Jawaban:

37

Pada kebanyakan filesystem¹, file secara unik ditentukan oleh nomor inode -nya , jadi yang perlu Anda periksa adalah apakah kedua file memiliki nomor inode yang sama dan berada di filesystem yang sama.

Ash, ksh, bash dan zsh memiliki konstruk yang melakukan pemeriksaan untuk Anda: operator kesetaraan file -ef.

[ fileA -ef fileB ] && ! [ fileA -ef fileC ]

Untuk kasus yang lebih lanjut, ls -i /path/to/filedaftarkan nomor inode file. df -P /path/to/filememperlihatkan sistem file tempat file itu berada (jika dua file berada di direktori yang sama, mereka berada di sistem file yang sama) Jika sistem Anda memiliki statperintah, mungkin ia dapat menampilkan nomor inode dan sistem file ( statbervariasi dari satu sistem ke sistem, periksa dokumentasi Anda). Jika Anda ingin melihat sekilas tautan keras di dalam direktori, coba ls -i | sort(mungkin disalurkan ke awk ).

¹ Semua filesystem asli unix, dan beberapa lainnya seperti NTFS, tetapi mungkin bukan kasus eksotis seperti CramFS.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Dan jelas bukan pada apa pun berbasis FAT, di mana ia akan terdeteksi sebagai file "cross-linked".
Ignacio Vazquez-Abrams
4
Perhatikan bahwa fileA -ef fileBjuga mengembalikan 0(sukses) jika fileAsymlink ke fileB, atau sebaliknya, atau keduanya terhubung ke file yang sama.
janmoesen
bagaimana Anda benar-benar menjalankan perintah yang Anda sarankan? Saya mencoba [.bashrc -ef .bash / .bashrc] dan variasi dari ini tetapi tidak berhasil.
Charlie Parker
@CharlieParker [ .bashrc -ef .bash/.bashrc ]benar. Tanpa konteks, tentu saja, saya tidak tahu mengapa itu “tidak benar-benar berfungsi” - Anda dapat membandingkan file yang salah, Anda mungkin tidak memeriksa hasilnya dengan benar, Anda dapat menggunakan shell tanpa -ef, ...
Gilles 'JADI berhenti berbuat jahat'
2
@CharlieParker Perintah ini sebenarnya [dan merupakan sinonim dari test. Tetapi man [atau man testakan memberikan Anda halaman manual dari perintah eksternal, sedangkan hampir semua shell di luar sana memiliki perintah built-in dengan opsi yang sedikit berbeda, jadi Anda perlu mencarinya di manual shell Anda.
Gilles 'SANGAT berhenti menjadi jahat'
4
function is-hardlinked() {
    r=yes
    [ "`stat -c '%i' $1`" != "`stat -c '%i' $2`" ] && r=no
    echo $r
}
arah x
sumber
6
Perhatikan bahwa ini bisa menjadi false positive jika kedua file berada pada sistem file yang berbeda tetapi kebetulan memiliki inode yang sama. Anda perlu menguji nomor perangkat juga ( stat -c %d). Dan jika Anda menggunakan Linux (diberi statperintah), shell Anda [ fileA -ef fileB ]harus melakukan semua ini secara langsung. Juga, perintah Anda secara tidak sengaja rusak dengan nama file yang mengandung spasi putih atau \[?*, atau dimulai dengan -: selalu menempatkan tanda kutip ganda di sekitar susbtitutions perintah ( "$(stat -c %i -- "$1")").
Gilles 'SANGAT berhenti menjadi jahat'
1
Mengapa Anda menggunakan sekelompok konstruksi yang tidak praktis tetapi portabel, dan kemudian functionkata kunci non-portabel dengan nama fungsi yang (karena mengandung tanda hubung) melanggar konvensi POSIX pada nama yang diizinkan?
Charles Duffy
Anda lupa mengutip $1dan $2. Anda mungkin juga ingin menggunakan $()sintaks daripada backtick karena tanda kurung memperjelas di mana perintah dimulai dan di mana berakhir dan bersarang lebih mudah.
josch
3

Seperti yang disarankan oleh poster pertama, Anda dapat menulis skrip berdasarkan sesuatu seperti ini di Linux:

stat -c '%i' fileA fileB fileC
Tidak sekarang
sumber
4
Ini tidak cukup: Anda akan mendapatkan nomor yang sama untuk dua file jika berada di sistem file yang berbeda tetapi kebetulan memiliki inode yang sama. Anda perlu menguji nomor perangkat juga ( stat -c %d). Dan jika Anda menggunakan Linux (diberi statperintah), shell Anda [ fileA -ef fileB ]harus melakukan semua ini secara langsung.
Gilles 'SANGAT berhenti menjadi jahat'
0

Dengan GNU find(1)versi 4.2.11 atau lebih baru Anda juga dapat menggunakan ini:

if [ "yes" = "$(find fileA -samefile fileB -exec echo yes \;)" ]; then
    echo yes
else
    echo no
fi

Jika fileAfile yang sama seperti fileBitu findakan mencetak "ya" dan kondisinya menjadi benar.

Berbeda dengan menggunakan operator kesetaraan file -efini akan menelurkan proses baru.

josch
sumber