題 你如何看到ls的實際硬鏈接?


我跑

ln /a/A /b/B

我想在文件夾中看到 a 文件A指向的位置 ls


82
2017-07-25 09:41


起源


硬鏈接不是指針,符號鏈接。它們是同一文件(inode)的多個名稱。之後 link(2) 系統調用,沒有任何意義,一個是原始的,一個是鏈接。這就是為什麼,正如答案所指出的,找到所有鏈接的唯一方法是 find / -samefile /a/A。因為inode的一個目錄條目不“了解”同一inode的其他目錄條目。他們所做的只是重新計算inode,以便在它的姓氏被刪除時刪除它 unlink(2)ed。 (這是“鏈接數”) ls 輸出)。 - Peter Cordes
@PeterCordes:refcount實際存儲在硬鏈接條目中嗎?這就是你的措辭所暗示的(“他們所做的只是重新計算inode ......”)但如果鏈接對彼此一無所知就沒有意義,因為當一個更新時,所有其他人都會以某種方式得到更新。或者refcount存儲在inode本身? (請原諒我,如果這是一個愚蠢的問題,我認為自己是一個新手,我還在學習)。 - loneboat
refcount存儲在inode中,正如您最終想到的那樣,必須是其他事實。 :)目錄條目被命名為inode的指針。當您有多個指向同一inode的名稱時,我們將其稱為“硬鏈接”。 - Peter Cordes


答案:


您可以找到您的文件的inode編號

ls -i

ls -l

顯示引用計數(特定inode的硬鏈接數)

找到inode編號後,您可以搜索具有相同inode的所有文件:

find . -inum NUM

將在當前目錄(。)中顯示inode NUM的文件名


149
2017-07-25 10:01



你可以運行find。 -samefile文件名 - BeowulfNode42
@ BeowulfNode42這個命令很棒,但它至少需要相同文件的共享根文件夾。 - Itachi
這個答案給出了一個務實的“做到這一點”,但我強烈地感受到了這一點 @LaurenceGonsalves答案 “如何”和/或“為什麼”的問題。 - Trevor Boyd Smith


你的問題沒有真正明確的答案。與符號鏈接不同,硬鏈接與“原始文件”無法區分。

目錄條目由文件名和指向inode的指針組成。 inode又包含文件元數據和(指向)實際文件內容)。創建硬鏈接會創建另一個文件名+對同一inode的引用。這些引用是單向的(至少在典型的文件系統中) - inode只保留引用計數。沒有內在的方法可以找出哪個是“原始”文件名。

順便說一句,這就是系統調用“刪除”文件的原因 unlink。它只是刪除了一個硬鏈接。僅當inode的引用計數降為0時,才會刪除inode附加數據。

找到給定inode的其他引用的唯一方法是窮舉搜索文件系統,檢查哪些文件引用了相關的inode。您可以使用shell中的“test A -ef B”來執行此檢查。


54
2017-07-25 09:51



這意味著 沒有到另一個文件的硬鏈接,因為原始文件也是一個硬鏈接;硬鏈接指向一個 磁盤上的位置。 - jtbandes
@jtbandes:硬鏈接指向指向實際數據的inode。 - dash17291


UNIX有硬鏈接和符號鏈接(用 "ln" 和 "ln -s" 分別)。符號鏈接只是一個文件,包含到另一個文件的真實路徑,可以跨文件系統。

從UNIX的早期開始就存在硬鏈接(無論如何我都記得,而且這種情況可能會持續很長時間)。它們是引用的兩個目錄條目 精確 相同的基礎數據。文件中的數據由其指定 inode。文件系統上的每個文件都指向一個inode,但並不要求每個文件都指向一個唯一的inode - 這就是硬鏈接的來源。

由於inode僅對給定的文件系統是唯一的,因此硬鏈接必須位於同一文件系統上(與符號鏈接不同)。請注意,與符號鏈接不同,沒有特權文件 - 它們都是相同的。數據區域僅在發佈時釋放 所有 刪除使用該inode的文件(並且所有進程也會關閉它,但這是一個不同的問題)。

你可以使用 "ls -i" 命令獲取特定文件的inode。然後你可以使用 "find <filesystemroot> -inum <inode>" 命令查找具有給定inode的文件系統上的所有文件。

這是一個完全相同的腳本。你調用它:

findhardlinks ~/jquery.js

它將找到該文件系統上的所有文件,這些文件是該文件的硬鏈接:

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

這是腳本。

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

32
2017-07-25 12:13



@pax:腳本中似乎有一個錯誤。我從它開始 . ./findhardlinks.bash 在OS X的Zsh中。屏幕中的當前窗口關閉。
@Masi問題是你的初始問題。 (與source命令相同)。這導致exit 1命令退出shell。使用chmod a + x findhardlinks.bash然後使用./findhardlinks.bash執行它或使用bash findhardlinks.bash - njsf
請看看我對你的答复的回复 superuser.com/questions/12972/to-see-hardlinks-by-ls/... - Léo Léopold Hertz 준영
要以編程方式執行此操作,如果您使用此代碼,則可能更具彈性: INUM=$(stat -c %i $1)。也 NUM_LINKS=$(stat -c %h $1)。看到 man stat 您可以使用更多格式變量。 - Joe
最好的答案,到目前為止。榮譽。 - MariusMatutiae


ls -l

第一列將表示權限。第二列將是子項的數量(對於目錄)或到文件的相同數據(硬鏈接,包括原始文件)的路徑數。例如:

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

23
2017-07-25 10:01



有助於確定給定文件是否具有[其他]硬鏈接,但不是它們所在的位置。 - mklement0


以下更簡單的怎麼樣? (後者可能會替換上面的長腳本!)

如果您有特定文件 <THEFILENAME>並想知道它遍布目錄的所有硬鏈接 <TARGETDIR>,(甚至可以是整個文件系統表示的 /

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

擴展邏輯,如果你想知道的所有文件 <SOURCEDIR> 有多個硬鏈接遍布 <TARGETDIR>

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

9
2017-08-15 08:52



這對我來說是最好的答案!但我不會用 -type f 因為該文件也可以是一個目錄。 - silvio
@silvio:你只能創建硬鏈接 檔,而不是目錄。 - mklement0
@ mklement0:你是對的! - silvio
該 . 和 .. 目錄中的條目是硬鏈接。您可以從鏈接計數中了解目錄中有多少個子目錄 .。無論如何,這是沒有意義的 find -samefile . 仍然不會打印任何 subdir/.. 輸出。 find (至少GNU版本)似乎是硬編碼忽略 .., 即使 -noleaf。 - Peter Cordes
此外,找到所有鏈接的想法是 O(n^2),並運行 find 一次為一組硬鏈接文件的每個成員。 find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate 會工作,(16不足以得到2 ^ 63-1的十進製表示,所以當你的XFS文件系統大到足以使inode數字高時,請注意) - Peter Cordes


腳本有很多答案可以找到文件系統中的所有硬鏈接。他們中的大多數都做傻事,比如運行find來掃描整個文件系統 -samefile 對於每個多重鏈接文件。這太瘋狂了;您只需要對inode編號進行排序並打印重複項。

find directories.. -xdev ! -type d -links +1 -printf '%20D %20i %p\n' | sort -n | uniq -w 42 --all-repeated=separate  (感謝@Tino調整我的原始命令以支持FS-id(%D),並處理所有非目錄文件類型,而不僅僅是常規文件。這將找到您的多重鏈接符號鏈,管道等)

運用 ! -type d -links +1 表示sort的輸入僅與uniq的最終輸出一樣大。除非您在僅包含一組硬鏈接的子目錄中運行它。無論如何,與其他任何發布的解決方案相比,這將使用少量CPU時間重新遍歷文件系統。

樣本輸出:

...
            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 ?:取消填充輸出。 uniq 具有非常有限的字段選擇支持,因此我填充查找輸出並使用固定寬度。對於最大可能的inode或設備編號,20chars足夠寬(2 ^ 64-1 = 18446744073709551615)。 XFS根據分配的磁盤位置選擇inode編號,而不是從0連續選擇,因此大型XFS文件系統可以具有> 32位的inode編號,即使它們沒有數十億個文件。其他文件系統可能具有20位的inode編號,即使它們不是巨大的。

TODO:按路徑對重複組進行排序。如果你有幾個不同的子目錄,有很多硬鏈接,讓他們按掛載點排序,然後inode數字混合在一起。 (即,一組重複組合在一起,但輸出混合起來)。

決賽 sort -k 3 將單獨的行分類,而不是作為單個記錄的行組。使用某些東西進行預處理,將一對換行轉換為NUL字節,並使用GNU sort --zero-terminated -k 3 可能會做的伎倆。 tr 但是,僅對單個字符進行操作,而不是2-> 1或1-> 2個模式。 perl 會這樣做(或只是在perl或awk中解析和排序)。 sed 也可能有用。


4
2018-04-21 19:32



%D 是文件系統標識符(它對於當前引導是唯一的,而沒有文件系統 umounted),所以下面更通用: find directories.. -xdev ! -type d -links +1 -printf '%20i %20D %p\n' | sort -n | uniq -w 42 --all-repeated=separate。這工作很長時間沒有給定的目錄包含文件系統級別的另一個目錄,它也會查看可以硬連接的所有內容(例如設備或軟鏈接 - 是的,軟鏈接的鏈接數大於1)。注意 dev_t 和 ino_t 今天是64位長。只要我們有64位系統,這可能會持續。 - Tino
@Tino:關於使用的好處 ! -type d, 代替 -type f。我甚至在我的文件系統上有一些硬鏈接的符號鏈接來組織一些文件集合。用你的改進版本更新了我的答案(但我把fs-id放在第一位,所以排序順序至少按文件系統分組。) - Peter Cordes


這有點是對Torocoro-Macho自己的答案和腳本的評論,但它顯然不適合評論框。


用更簡單的方法重寫您的腳本以查找信息,從而減少了很少的進程調用。

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

為了便於比較,我盡量使它與你的相似。

評論這個腳本和你的腳本

  • 人們應該總是避免 $IFS 如果一個glob足夠了,因為它不必要地複雜化,並且文件名實際上可以包含換行符(但實際上主要是第一個原因)。

  • 您應該避免手動解析 ls 這樣的輸出盡可能多,因為它遲早會咬你。例如:在你的第一個 awk在所有包含空格的文件名上失敗。

  • printf 最終會挽救麻煩,因為它非常強大 %s 句法。它還可以讓您完全控制輸出,並且保持一致 所有 系統,不像 echo

  • stat 在這種情況下可以為您節省很多邏輯。

  • GNU find 是強大的。

  • 您的 head 和 tail 調用可以直接處理 awk 與...該 exit 命令和/或選擇 NR 變量。這樣可以節省進程調用,這幾乎總是在硬編寫腳本中嚴重提高性能。

  • 您的 egreps也可以是公正的 grep


3
2018-06-13 07:40



xDEVICE = $(stat -c%m“$ {xFILE}”)不適用於所有系統(例如:stat(GNU coreutils)6.12)。如果腳本輸出“Item:?”在每行的前面,然後用更像原始腳本的行替換這個違規行,但是將xITEM重命名為xFILE:xDEVICE = $(df“$ {xFILE}”| tail -1l | awk'{print $ 6} ') - kbulgrien
如果您只想要一組硬鏈接,而不是將每個成員重複為“主”,請使用 find ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate。這要快得多,因為它只遍歷fs一次。對於同時存在多個FS,您需要在inode編號前加上FS ID。也許有 find -exec stat... -printf ... - Peter Cordes
把這個想法變成了答案 - Peter Cordes


基於 findhardlinks 腳本(將其重命名為 hard-links),這是我重構並使其工作的原因。

輸出:

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

2
2017-11-16 22:46



我在這個腳本上發表評論作為單獨的答案。 - Daniel Andersson


GUI解決方案非常接近您的問題:

您無法列出“ls”中的實際硬鏈接文件,因為正如之前的評論員指出的那樣,文件“名稱”僅僅是同一數據的別名。然而,實際上有一個GUI工具非常接近你想要的,它顯示了一個指向linux下相同數據(如硬鏈接)的文件名路徑列表,它被稱為FSLint。您想要的選項是在“名稱衝突” - >在搜索(XX) - >中取消選擇“複選框$ PATH”,然後從“for ...”後面的下拉框中選擇“Aliases”到“top-middle”。

FSLint記錄很差,但我發現在“搜索路徑”下確保有限的目錄樹,並選中“Recurse?”複選框。在上述選項中,在程序搜索之後產生具有“指向”相同數據的路徑和名稱的硬鏈接數據的列表。


1
2018-01-20 18:00



FSlint可以在 pixelbeat.org/fslint - mklement0


你可以配置 ls 使用“別名”突出顯示硬鏈接,但如前所述,無法顯示硬鏈接的“來源”,這就是我追加的原因 .hardlink 幫助。

highlight hardlinks

在你的某處添加以下內容 .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'

1
2017-12-06 17:34