tôi chạy
ln /a/A /b/B
Tôi muốn xem tại thư mục a
mà tệp A trỏ tới ls
.
tôi chạy
ln /a/A /b/B
Tôi muốn xem tại thư mục a
mà tệp A trỏ tới ls
.
Câu trả lời:
Bạn có thể tìm thấy số inode cho tập tin của bạn với
ls -i
và
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 (.)
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.
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"
và "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
. ./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.
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.
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
inode
điểm lần lượt trỏ đến nội dung đĩa.
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 {} \;
-type f
vì tập tin cũng có thể là một thư mục.
.
và ..
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
.
O(n^2)
và chạy find
mộ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=separate
sẽ 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)
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 -samefile
cho 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 /bar
là 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 d
từ chối thư mục: .
và ..
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ốngSử dụng ! -type d -links +1
có 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 awk
hoặc cut
. uniq
có 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 3
sẽ 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 3
có thể thực hiện thủ thuật. tr
Tuy 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. perl
sẽ làm điều đó (hoặc chỉ phân tích cú pháp và sắp xếp trong perl hoặc awk). sed
cũng có thể làm việc
%D
là đị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à umount
ed), 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_t
và ino_t
dà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.
! -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.)
Đâ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.
Người ta phải luôn luôn tránh $IFS
phé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 ls
và đầ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 awk
dò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.
printf
thường sẽ lưu các rắc rối cuối cùng vì nó rất mạnh với %s
cú 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 head
và tail
có thể đã được xử lý trực tiếp awk
bằng ví dụ exit
lệnh và / hoặc chọn trên NR
biế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 egrep
cũng có thể như vậy grep
.
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 ...
Dựa trên findhardlinks
kị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 "";
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.
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 .hardlink
vào để trợ giúp điều đó.
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'
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" trongls
đầu ra).