Làm thế nào bạn có thể thấy các liên kết cứng thực tế của ls?


97

tôi chạy

ln /a/A /b/B

Tôi muốn xem tại thư mục amà tệp A trỏ tới ls.


1
Liên kết cứng không phải là con trỏ, symlink là. Chúng là nhiều tên cho cùng một tệp (inode). Sau một link(2)cuộc gọi hệ thống, không có ý nghĩa trong đó một là bản gốc và một là liên kết. Đây là lý do tại sao, như các câu trả lời chỉ ra, cách duy nhất để tìm thấy tất cả các liên kết là find / -samefile /a/A. Bởi vì một mục nhập thư mục cho một nút không "biết về" các mục nhập thư mục khác cho cùng một nút. Tất cả những gì họ làm là giới thiệu inode để nó có thể bị xóa khi tên cuối cùng của nó là unlink(2)ed. (Đây là "số lượng liên kết" trong lsđầu ra).
Peter Cordes

@PeterCordes: Là số tiền thực sự được lưu trữ trong mục nhập liên kết cứng? Đó là những gì từ ngữ của bạn ngụ ý ("Tất cả những gì họ làm là từ bỏ nút in ...") Nhưng điều đó sẽ không có ý nghĩa nếu các liên kết không biết gì về nhau, vì khi một người cập nhật, tất cả những người khác sẽ phải đã được cập nhật. Hoặc là số tiền được lưu trữ trong chính inode? (Hãy tha thứ cho tôi nếu đó là một câu hỏi ngớ ngẩn, tôi coi mình là người mới và tôi vẫn đang học).
loneboat

1
Số tiền được lưu trữ trong inode, vì cuối cùng bạn đã tìm ra phải là trường hợp, từ các sự kiện khác. :) Mục nhập thư mục được đặt tên con trỏ đến inodes. Chúng tôi gọi đó là "liên kết cứng" khi bạn có nhiều tên trỏ đến cùng một nút.
Peter Cordes

Câu trả lời:


171

Bạn có thể tìm thấy số inode cho tập tin của bạn với

ls -i

ls -l

hiển thị số tham chiếu (số lượng liên kết cứng đến một nút cụ thể)

sau khi bạn tìm thấy số inode, bạn có thể tìm kiếm tất cả các tệp có cùng một nút:

find . -inum NUM

sẽ hiển thị tên tệp cho inode NUM trong thư mục hiện tại (.)


46
bạn chỉ có thể chạy tìm. Tên tệp -samefile
BeowulfNode42

1
@ BeowulfNode42 Lệnh này rất hay, nhưng ít nhất nó cần thư mục gốc được chia sẻ của cùng một tệp.
Itachi

1
câu trả lời này mang đến một cách thực hiện "làm điều này" nhưng tôi cảm thấy mạnh mẽ rằng @LaurenceGonsalves trả lời các câu hỏi "làm thế nào" và / hoặc "tại sao".
Trevor Boyd Smith

65

Thực sự không có câu trả lời rõ ràng cho câu hỏi của bạn. Không giống như symlink, liên kết cứng không thể phân biệt được với "tập tin gốc".

Các mục thư mục bao gồm một tên tệp và một con trỏ tới một nút. Lần lượt inode chứa siêu dữ liệu tệp và (con trỏ tới) nội dung tệp thực tế). Tạo một liên kết cứng tạo ra một tên tệp + tham chiếu đến cùng một nút. Các tham chiếu này là đơn hướng (ít nhất là trong các hệ thống tệp điển hình) - inode chỉ giữ số tham chiếu. Không có cách nội tại để tìm ra tên tệp "gốc".

Nhân tiện, đây là lý do tại sao hệ thống gọi "xóa" một tập tin được gọi unlink. Nó chỉ loại bỏ một liên kết cứng. Inode một dữ liệu đính kèm chỉ bị xóa nếu số tham chiếu của inode giảm xuống 0.

Cách duy nhất để tìm các tham chiếu khác đến một nút đã cho là tìm kiếm toàn bộ hệ thống tệp kiểm tra xem các tệp nào đề cập đến nút được đề cập. Bạn có thể sử dụng 'test A -ef B' từ shell để thực hiện kiểm tra này.


35
Điều đó có nghĩa là không có thứ gọi là liên kết cứng đến tập tin khác , vì tập tin gốc cũng là một liên kết cứng; liên kết cứng trỏ đến một vị trí trên đĩa .
jtbandes

12
@jtbandes: Liên kết cứng trỏ đến một nút in trỏ đến dữ liệu thực tế.
dash17291

33

UNIX có các liên kết cứng và liên kết tượng trưng (được tạo bằng "ln""ln -s"tương ứng). Liên kết tượng trưng chỉ đơn giản là một tệp chứa đường dẫn thực đến tệp khác và có thể vượt qua các hệ thống tệp.

Các liên kết cứng đã xuất hiện từ những ngày đầu tiên của UNIX (dù sao tôi cũng có thể nhớ và điều đó sẽ quay trở lại khá lâu). Chúng là hai mục thư mục tham chiếu chính xác cùng một dữ liệu cơ bản. Dữ liệu trong một tệp được chỉ định bởi nó inode. Mỗi tệp trên một hệ thống tệp trỏ đến một nút nhưng không có yêu cầu nào mỗi tệp chỉ đến một nút duy nhất - đó là nơi các liên kết cứng xuất phát.

Vì các nút chỉ là duy nhất cho một hệ thống tệp nhất định, nên có một hạn chế là các liên kết cứng phải nằm trên cùng một hệ thống tệp (không giống như các liên kết tượng trưng). Lưu ý rằng, không giống như các liên kết tượng trưng, ​​không có tệp đặc quyền - tất cả chúng đều bằng nhau. Vùng dữ liệu sẽ chỉ được giải phóng khi tất cả các tệp sử dụng inode đó bị xóa (và tất cả các quy trình cũng đóng nó, nhưng đó là một vấn đề khác).

Bạn có thể sử dụng "ls -i"lệnh để lấy inode của một tệp cụ thể. Sau đó, bạn có thể sử dụng "find <filesystemroot> -inum <inode>"lệnh để tìm tất cả các tệp trên hệ thống tệp với inode đã cho.

Đây là một kịch bản thực hiện chính xác điều đó. Bạn gọi nó bằng:

findhardlinks ~/jquery.js

và nó sẽ tìm thấy tất cả các tệp trên hệ thống tệp đó là các liên kết cứng cho tệp đó:

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

Đây là kịch bản.

#!/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: Dường như có một lỗi trong kịch bản. Tôi bắt đầu bằng cách . ./findhardlinks.bashở trong Zsh của OS X. Cửa sổ hiện tại của tôi trong Màn hình đóng lại.

4
@Masi Vấn đề là ban đầu của bạn. (giống như lệnh nguồn). Điều đó làm cho lệnh exit 1 thoát khỏi shell của bạn. Sử dụng chmod a + x findhardlinks.bash sau đó thực hiện nó với ./findhardlinks.bash hoặc sử dụng bash findhardlinks.bash
njsf

Xin vui lòng, xem trả lời của tôi cho câu trả lời của bạn tại superuser.com/questions/12972/to-see-hardlinks-by-ls/...
Léo Léopold Hertz 준영

3
Để thực hiện việc này theo lập trình, có lẽ sẽ linh hoạt hơn nếu bạn sử dụng điều này thay thế : INUM=$(stat -c %i $1). Ngoài ra NUM_LINKS=$(stat -c %h $1). Xem man statđể biết thêm các biến định dạng bạn có thể sử dụng.
Joe

Câu trả lời tốt nhất, cho đến nay. Thanh danh.
MariusMatutiae

24
ls -l

Cột đầu tiên sẽ đại diện cho quyền. Cột thứ hai sẽ là số lượng các mục con (đối với thư mục) hoặc số đường dẫn đến cùng một dữ liệu (liên kết cứng, bao gồm tệp gốc) đến tệp. Ví dụ:

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

2
Hữu ích trong việc xác định NẾU một tệp nhất định có [khác] liên kết cứng, nhưng không phải là chúng ở đâu.
mkuity0

Ngoài ra, không có sự phân biệt kỹ thuật giữa một liên kết cứng và một tập tin gốc. Cả hai đều giống hệt nhau ở chỗ chúng chỉ đơn giản là trỏ đến inodeđiểm lần lượt trỏ đến nội dung đĩa.
Guyarad

13

Làm thế nào về một trong những đơn giản sau đây? (Latter có thể thay thế các đoạn script dài ở trên!)

Nếu bạn có một tệp cụ thể <THEFILENAME>và muốn biết tất cả các liên kết cứng của nó trải đều trên thư mục <TARGETDIR>, (thậm chí có thể là toàn bộ hệ thống tệp được biểu thị bởi /)

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

Mở rộng logic, nếu bạn muốn biết tất cả các tệp trong việc <SOURCEDIR>có nhiều liên kết cứng trải rộng <TARGETDIR>:

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

Đây là cho tôi câu trả lời tốt nhất! nhưng tôi sẽ không sử dụng -type fvì tập tin cũng có thể là một thư mục.
silvio

3
@silvio: Bạn chỉ có thể tạo liên kết cứng đến tệp chứ không phải thư mục.
mkuity0

@ mkuity0: Bạn nói đúng!
silvio

Các mục ...trong thư mục là liên kết cứng. Bạn có thể cho biết có bao nhiêu thư mục con trong một thư mục từ số lượng liên kết .. Dù sao đây cũng là bản dựng, vì find -samefile .vẫn không in bất kỳ subdir/..đầu ra nào . find(ít nhất là phiên bản GNU) dường như được mã hóa cứng để bỏ qua .., ngay cả với -noleaf.
Peter Cordes

Ngoài ra, ý tưởng tìm tất cả các liên kết đó là O(n^2)và chạy findmột lần cho mỗi thành viên của một tập hợp các tệp liên kết cứng. find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separatesẽ hoạt động, (16 không đủ rộng để biểu diễn thập phân 2 ^ 63-1, vì vậy khi hệ thống tệp XFS của bạn đủ lớn để có số inode cao, hãy coi chừng)
Peter Cordes

5

Có rất nhiều câu trả lời với các tập lệnh để tìm tất cả các liên kết cứng trong một hệ thống tập tin. Hầu hết trong số họ làm những việc ngớ ngẩn như chạy find để quét toàn bộ hệ thống tệp -samefilecho EACH tệp được liên kết nhiều lần. Điều này là điên; tất cả những gì bạn cần là sắp xếp số inode và in các bản sao.

Chỉ với một lần vượt qua hệ thống tệp để tìm và nhóm tất cả các bộ tệp liên kết cứng

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

Điều này nhanh hơn nhiều so với các câu trả lời khác để tìm nhiều bộ tệp liên kết cứng.
find /foo -samefile /barlà tuyệt vời cho chỉ một tập tin.

  • -xdev: giới hạn trong một hệ thống tập tin. Không thực sự cần thiết vì chúng tôi cũng in id-id sang uniq trên
  • ! -type dtừ chối thư mục: ...các mục có nghĩa là chúng luôn được liên kết.
  • -links +1 : số lượng liên kết nghiêm ngặt > 1
  • -printf ...in FS-id, số inode và đường dẫn. (Với phần đệm cho chiều rộng cột cố định mà chúng ta có thể biết uniq.)
  • sort -n | uniq ... sắp xếp số và duy nhất trên 42 cột đầu tiên, phân tách các nhóm bằng một dòng trống

Sử dụng ! -type d -links +1có nghĩa là đầu vào của sắp xếp chỉ lớn bằng đầu ra cuối cùng của uniq vì vậy chúng tôi không thực hiện một số lượng lớn sắp xếp chuỗi. Trừ khi bạn chạy nó trên thư mục con chỉ chứa một trong các tập hợp liên kết cứng. Dù sao, điều này sẽ sử dụng rất ít thời gian CPU để duyệt lại hệ thống tập tin hơn bất kỳ giải pháp được đăng nào khác.

đầu ra mẫu:

...
            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?: Bỏ đệm đầu ra bằng awkhoặc cut. uniqcó hỗ trợ chọn trường rất hạn chế, vì vậy tôi đệm đầu ra tìm và sử dụng chiều rộng cố định. 20chars đủ rộng cho số inode hoặc số thiết bị tối đa có thể (2 ^ 64-1 = 18446744073709551615). XFS chọn số inode dựa trên vị trí trên đĩa được phân bổ, không liên tục từ 0, vì vậy các hệ thống tệp XFS lớn có thể có số inode 32 bit ngay cả khi chúng không có hàng tỷ tệp. Các hệ thống tập tin khác có thể có số inode 20 chữ số ngay cả khi chúng không khổng lồ.

TODO: sắp xếp các nhóm trùng lặp theo đường dẫn. Có chúng được sắp xếp theo điểm gắn kết sau đó số inode trộn mọi thứ lại với nhau, nếu bạn có một vài thư mục con khác nhau có nhiều liên kết cứng. (tức là các nhóm của các nhóm song song đi cùng nhau, nhưng đầu ra trộn chúng lại với nhau).

Một trận chung kết sort -k 3sẽ sắp xếp các dòng riêng biệt, không phải các nhóm dòng dưới dạng một bản ghi. Tiền xử lý với một cái gì đó để chuyển đổi một cặp dòng mới thành một byte NUL và sử dụng GNU sort --zero-terminated -k 3có thể thực hiện thủ thuật. trTuy nhiên, chỉ hoạt động trên các ký tự đơn lẻ, không phải mẫu 2-> 1 hoặc 1-> 2. perlsẽ làm điều đó (hoặc chỉ phân tích cú pháp và sắp xếp trong perl hoặc awk). sedcũng có thể làm việc


1
%Dlà định danh hệ thống tập tin (nó là duy nhất cho khởi động hiện tại trong khi không có hệ thống tập tin nào là umounted), do đó, sau đây thậm chí còn chung chung hơn : find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate. Điều này hoạt động miễn là không có thư mục đã cho nào chứa thư mục khác ở cấp hệ thống tệp, nó cũng xem xét mọi thứ có thể được liên kết cứng (như thiết bị hoặc liên kết mềm - vâng, liên kết mềm có thể có số lượng liên kết lớn hơn 1). Lưu ý rằng dev_tino_tdài 64 bit ngày hôm nay. Điều này có thể sẽ giữ miễn là chúng ta có hệ thống 64 bit.
Tino

@Tino: điểm tuyệt vời về việc sử dụng ! -type d, thay vì -type f. Tôi thậm chí có một số liên kết tượng trưng liên kết cứng trên hệ thống tệp của mình từ việc tổ chức một số bộ sưu tập tệp. Đã cập nhật câu trả lời của tôi với phiên bản cải tiến của bạn (nhưng tôi đặt fs-id trước, vì vậy thứ tự sắp xếp ít nhất là các nhóm theo hệ thống tệp.)
Peter Cordes

3

Đây là một phần của một nhận xét cho câu trả lời và kịch bản riêng của Torocoro-Macho, nhưng rõ ràng nó không phù hợp trong hộp bình luận.


Viết lại tập lệnh của bạn với các cách đơn giản hơn để tìm thông tin, và do đó, ít yêu cầu xử lý hơn.

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

Tôi đã cố gắng giữ nó giống với bạn nhất có thể để so sánh dễ dàng.

Nhận xét về kịch bản này và của bạn

  • Người ta phải luôn luôn tránh $IFSphép thuật nếu một quả cầu đủ, vì nó bị xáo trộn không cần thiết và tên tệp thực sự có thể chứa dòng mới (nhưng thực tế chủ yếu là lý do đầu tiên).

  • Bạn nên tránh phân tích cú pháp bằng tay lsvà đầu ra như vậy càng nhiều càng tốt, vì nó sớm muộn gì cũng sẽ cắn bạn. Ví dụ: trong awkdòng đầu tiên của bạn , bạn thất bại trên tất cả các tên tệp có chứa khoảng trắng.

  • printfthường sẽ lưu các rắc rối cuối cùng vì nó rất mạnh với %scú pháp. Nó cũng cung cấp cho bạn toàn quyền kiểm soát đầu ra và nhất quán trên tất cả các hệ thống, không giống như echo.

  • stat có thể giúp bạn tiết kiệm rất nhiều logic trong trường hợp này.

  • GNU find là mạnh mẽ.

  • Yêu cầu của bạn headtailcó thể đã được xử lý trực tiếp awkbằng ví dụ exitlệnh và / hoặc chọn trên NRbiến. Điều này sẽ tiết kiệm các yêu cầu quy trình, mà hầu như luôn luôn thực hiện nghiêm túc hiệu suất trong các kịch bản làm việc chăm chỉ.

  • Bạn egrepcũng có thể như vậy grep.


xDEVICE = $ (stat -c% m "$ {xFILE}") không hoạt động trên tất cả các hệ thống (ví dụ: stat (GNU coreutils) 6.12). Nếu tập lệnh xuất ra "Mục :?" ở đầu mỗi dòng, sau đó thay thế dòng vi phạm này bằng một dòng giống với tập lệnh gốc, nhưng với xITEM được đổi tên thành xFILE: xDEVICE = $ (df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien

Nếu bạn chỉ muốn các nhóm liên kết cứng, thay vì lặp lại với mỗi thành viên là "chủ", hãy sử dụng find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate. Đây là NHIỀU nhanh hơn, vì nó chỉ đi qua fs một lần. Đối với nhiều FS cùng một lúc, bạn cần phải thêm tiền tố vào các số inode bằng id FS. Có thể vớifind -exec stat... -printf ...
Peter Cordes

biến ý tưởng đó thành một câu trả lời
Peter Cordes

2

Dựa trên findhardlinkskịch bản (được đổi tên thành hard-links), đây là những gì tôi đã tái cấu trúc và làm cho nó hoạt động.

Đầu ra:

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

Tôi đã đăng ý kiến ​​về kịch bản này như một câu trả lời riêng biệt.
Daniel Andersson

1

Một giải pháp GUI thực sự gần với câu hỏi của bạn:

Bạn không thể liệt kê các tệp liên kết cứng thực tế từ "ls" bởi vì, như các nhà bình luận trước đây đã chỉ ra, "tên" tệp chỉ là bí danh cho cùng một dữ liệu. Tuy nhiên, thực sự có một công cụ GUI thực sự gần với những gì bạn muốn, đó là hiển thị một danh sách đường dẫn các tên tệp trỏ đến cùng một dữ liệu (như các liên kết cứng) trong linux, nó được gọi là FSLint. Tùy chọn bạn muốn nằm trong "Xung đột tên" -> bỏ chọn "hộp kiểm $ PATH" trong Tìm kiếm (XX) -> và chọn "Bí danh" từ hộp thả xuống sau "cho ..." ở giữa trên cùng.

FSLint được ghi lại rất kém nhưng tôi thấy rằng việc đảm bảo cây thư mục giới hạn trong "Đường dẫn tìm kiếm" với hộp kiểm được chọn cho "Recurse?" và các tùy chọn đã nói ở trên, một danh sách dữ liệu được liên kết cứng với các đường dẫn và tên "trỏ" đến cùng một dữ liệu được tạo sau khi tìm kiếm chương trình.


Có thể tìm thấy FSlint
mkuity0

1

Bạn có thể định cấu hình lsđể làm nổi bật các liên kết cứng bằng cách sử dụng một 'bí danh', nhưng như đã nêu trước đây, không có cách nào để hiển thị 'nguồn' của liên kết cứng đó là lý do tại sao tôi thêm .hardlinkvào để trợ giúp điều đó.

làm nổi bật các liên kết cứng

Thêm vào đây một nơi nào đó trong của bạn .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.