どのようにしてlsで実際のハードリンクを見ることができますか?


97

走る

ln /a/A /b/B

aファイルAが指すフォルダーを参照したいのですがls


1
ハードリンクはポインターではなく、シンボリックリンクです。同じファイル(inode)の複数の名前です。link(2)システムコールの後、1つがオリジナルで1つがリンクであるという意味はありません。これが、答えが指摘しているように、すべてのリンクを見つける唯一の方法がである理由ですfind / -samefile /a/A。iノードの1つのディレクトリエントリは、同じiノードの他のディレクトリエントリを「認識」していないためです。彼らがするのは、iノードを参照カウントするだけですunlink(2)ed。(これはls出力の「リンクカウント」です)。
ピーター・コーデス

@PeterCordes:refcountは実際にハードリンクエントリに保存されていますか?それはあなたの言葉遣いが意味するものです(「彼らがすることはすべてiノードを参照することです...」)更新します。または、refcountはiノード自体に保存されていますか?(それが馬鹿げた質問なら私を許してください、私は自分を初心者と考えています、そして、私はまだ学んでいます)。
ローンボート

1
refcountはiノードに格納されますが、他の事実から最終的にはそうであることがわかるはずです。:)ディレクトリエントリは、iノードへの名前付きポインタです。同じiノードを指す複数の名前がある場合、「ハードリンク」と呼びます。
ピーターコーデス

回答:


171

ファイルのinode番号を見つけるには

ls -i

そして

ls -l

参照カウント(特定のiノードへのハードリンクの数)を表示します

iノード番号を見つけたら、同じiノードを持つすべてのファイルを検索できます。

find . -inum NUM

現在のディレクトリ(。)のiノードNUMのファイル名を表示します


46
findを実行するだけです。-samefileファイル名
-BeowulfNode42

1
@ BeowulfNode42このコマンドは素晴らしいですが、少なくとも同じファイルの共有ルートフォルダーが必要です。
イタチ

1
この回答は実用的な「これを行う」ことを意味しますが@ LaurenceGonsalvesが「方法」および/または「理由」の質問に答えることを強く感じています。
トレバーボイドスミス

65

あなたの質問に対する明確な答えは実際にはありません。シンボリックリンクとは異なり、ハードリンクは「元のファイル」と区別できません。

ディレクトリエントリは、ファイル名とiノードへのポインタで構成されます。iノードには、ファイルのメタデータと(実際のファイルの内容へのポインター)が含まれます。ハードリンクを作成すると、別のファイル名+同じiノードへの参照が作成されます。これらの参照は単方向です(少なくとも典型的なファイルシステムでは)-iノードは参照カウントのみを保持します。「元の」ファイル名を見つけるための固有の方法はありません。

ところで、これが、ファイルを「削除」するシステムコールが呼び出される理由unlinkです。ハードリンクを削除するだけです。接続されたデータのiノードは、iノードの参照カウントが0になった場合にのみ削除されます。

特定のiノードへの他の参照を見つける唯一の方法は、ファイルシステムを徹底的に検索して、問題のiノードを参照しているファイルを確認することです。このチェックを実行するには、シェルから「test A -ef B」を使用できます。


35
つまり、元のファイルもハードリンクであるため、別のファイルへのハードリンクなどはありません。ハードリンクは、ディスク上場所を指します。
jtbandes 09

12
@jtbandes:ハードリンクは、実際のデータを指すiノードを指します。
dash17291

33

UNIXにはハードリンクとシンボリックリンクがあります(それぞれ"ln"とで作成され"ln -s"ます)。シンボリックリンクは、別のファイルへの実際のパスを含む単なるファイルであり、ファイルシステムを横断できます。

ハードリンクは、UNIXの初期から存在していました(とにかく記憶できますが、それはかなり前に遡ります)。これらは、まったく同じ基礎データを参照する2つのディレクトリエントリです。ファイル内のデータは、そので指定されますinode。ファイルシステム上の各ファイルはiノードをポイントしますが、各ファイルが一意のiノードをポイントする必要はありません。ハードリンクはここから取得されます。

iノードは特定のファイルシステムに対してのみ一意であるため、ハードリンクは同じファイルシステム上になければならないという制限があります(シンボリックリンクとは異なります)。シンボリックリンクとは異なり、特権ファイルはないことに注意してください-それらはすべて同じです。データ領域は、そのiノードを使用しているすべてのファイルが削除されたときにのみ解放されます(すべてのプロセスも同様に閉じますが、これは別の問題です)。

この"ls -i"コマンドを使用して、特定のファイルのiノードを取得できます。その後、"find <filesystemroot> -inum <inode>"コマンドを使用して、指定されたiノードを持つファイルシステム上のすべてのファイルを検索できます。

これを正確に行うスクリプトを次に示します。あなたはそれを呼び出します:

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

@pax:スクリプトにバグがあるようです。. ./findhardlinks.bashOS XのZshにいる間に開始します。Screenの現在のウィンドウが閉じます。

4
@Masi問題はあなたの初期です。(sourceコマンドと同じ)。これにより、exit 1コマンドがシェルを終了します。その後、./findhardlinks.bashでそれを実行したり、bashのfindhardlinks.bash使用のchmod A + X findhardlinks.bashを使用してください
njsf

、であなたの答えに私の返事を参照してくださいsuperuser.com/questions/12972/to-see-hardlinks-by-ls/...
レオ・レオポルド・ヘルツ준 영

3
プログラムでこれを行うには、代わりにこれを使用すると、おそらくより回復力がありますINUM=$(stat -c %i $1)。またNUM_LINKS=$(stat -c %h $1)man stat使用できるその他の形式変数を参照してください。
ジョー

最良の答え、断然。称賛。
MariusMatutiae

24
ls -l

最初の列は許可を表します。2番目の列は、サブアイテムの数(ディレクトリの場合)またはファイルへの同じデータ(元のファイルを含むハードリンク)へのパスの数になります。例えば:

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

2
特定のファイルに[他の]ハードリンクがあるかどうかを判断するのに役立ちますが、どこにあるかはわかりません。
mklement0

また、ハードリンクと元のファイルに技術的な違いはありません。どちらもinodeディスクコンテンツを指すだけであるという点で、どちらも同じです。
ガイアラド

13

次の簡単なものはどうですか?(後者は上の長いスクリプトを置き換えるかもしれません!)

特定のファイルが<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 {} \; 

これは私にとって最高の答えです!しかし-type f、ファイルもディレクトリになる可能性があるため、使用しません。
silvio

3
@silvio:ディレクトリへのリンクではなく、ファイルへのハードリンクのみを作成できます。
mklement0

@ mklement0:そのとおりです!
silvio

ディレクトリ内の.および..エントリはハードリンクです。ディレクトリ内のサブディレクトリの数は、のリンクカウントから知ることができます.find -samefile .それでもsubdir/..出力は出力されないため、これはとにかく意味がありません。 find(少なくともGNUバージョン)は..、でも無視するようにハードコードされているよう-noleafです。
ピーターコーデス

また、そのfind-all-linksアイデアはO(n^2)でありfind、ハードリンクファイルのセットの各メンバーに対して1回実行されます。 find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate(16は2 ^ 63-1の10進数表現に十分な幅がないため、XFSファイルシステムが十分に大きいiノード番号を持っている場合は注意してください)
Peter Cordes

5

ファイルシステム内のすべてのハードリンクを見つけるためのスクリプトには多くの答えがあります。それらのほとんどは、findを実行し-samefileて各多重リンクファイルのファイルシステム全体をスキャンするなど、ばかげたことをします。狂ってる; 必要なのは、iノード番号でソートし、複製を印刷することだけです。

ハードリンクされたファイルのすべてのセットを見つけてグループ化するために、ファイルシステムを1回パスするだけで

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

これは、ハードリンクされたファイルの複数のセットを見つけるための他の回答よりもはるかに高速です。
find /foo -samefile /bar1つのファイルに最適です。

  • -xdev:1つのファイルシステムに制限します。FS-idもuniqに出力するため、厳密には必要ありません
  • ! -type dディレクトリを拒否する:...エントリは、それらが常にリンクされていることを意味します。
  • -links +1 :厳密にリンク数 > 1
  • -printf ...FS-id、iノード番号、およびパスを出力します。(通知できる固定列幅へのパディングを使用しますuniq。)
  • sort -n | uniq ... グループを空白行で区切る、最初の42列での数値の並べ替えと一意化

を使用! -type d -links +1すると、ソートの入力はuniqの最終出力と同じ大きさになるため、大量の文字列ソートは実行されません。ハードリンクのセットの1つのみを含むサブディレクトリで実行しない限り。とにかく、これは他の投稿されたソリューションよりもはるかに少ない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 ?: awkまたはで出力の埋め込みを解除しcutます。 uniqフィールド選択のサポートは非​​常に限られているため、検索出力を埋め込み、固定幅を使用します。20文字は、可能な最大のiノードまたはデバイス番号(2 ^ 64-1 = 18446744073709551615)に十分な幅です。XFSは、0から連続してではなく、ディスク上の割り当てられた場所に基づいてiノード番号を選択するため、大規模なXFSファイルシステムは、数十億のファイルがなくても32ビットを超えるiノード番号を持つことができます。他のファイルシステムは、巨大ではない場合でも20桁のiノード番号を持っている場合があります。

TODO:重複のグループをパスでソートします。多数のハードリンクを含むいくつかの異なるサブディレクトリがある場合、マウントポイントでソートしてからiノード番号で物事を混合します。(つまり、dup-groupsのグループは一緒になりますが、出力はそれらをミックスします)。

ファイナルsort -k 3は、行のグループを単一のレコードとしてではなく、行を個別にソートします。一対の改行をNULバイトに変換するために何かを前処理し、GNUを使用sort --zero-terminated -k 3することでうまくいくかもしれません。 trただし、2-> 1または1-> 2のパターンではなく、単一の文字でのみ動作します。 perlそれを行います(または単にperlまたはawk内で解析およびソートします)。 sedうまくいくかもしれません。


1
%Dはファイルシステム識別子です(ファイルシステムがumount編集されていない間は現在のブートに対して一意です)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:の! -type d代わりにを使用することの素晴らしい点-type f。ファイルのコレクションを整理することで、ハードリンクされたシンボリックリンクがファイルシステムにあります。改善されたバージョンで私の回答を更新しました(ただし、fs-idを最初に置いたので、ソート順は少なくともファイルシステムごとにグループ化します。)
Peter Cordes

3

これは、トロコロ・マッチョ自身の回答とスクリプトに対するコメントですが、コメントボックスには収まりません。


情報を見つけるためのより簡単な方法でスクリプトを書き直し、プロセス呼び出しを大幅に減らします。

#!/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グロブで十分な場合は、不必要に複雑になり、実際にはファイル名に改行が含まれる可能性があるため、マジックを常に回避する必要があります(ただし、実際にはほとんどが最初の理由です)。

  • ls遅かれ早かれあなたに噛み付くので、手動での解析とそのような出力は可能な限り避けるべきです。たとえば、最初のawk行では、スペースを含むすべてのファイル名で失敗します。

  • printf%s構文に対して非常に堅牢であるため、多くの場合、最終的にトラブルを回避できます。また、出力を完全に制御でき、とは異なり、すべてのシステムで一貫してますecho

  • stat この場合、多くのロジックを節約できます。

  • GNU find 強力です。

  • あなたheadtail呼び出しがで直接処理されている可能性awkなどでexitコマンドおよび/または上で選択するNR変数。これにより、プロセス呼び出しが保存され、ほとんどの場合、勤勉なスクリプトのパフォーマンスが大幅に向上します。

  • あなたegrepのものはちょうど同様であるかもしれませんgrep


xDEVICE = $(stat -c%m "$ {xFILE}")は、すべてのシステムで機能しません(例:stat(GNU coreutils)6.12)。スクリプトが「Item:?」を出力した場合 各行の先頭で、この問題のある行を元のスクリプトに似た行に置き換えますが、xITEMはxFILEに名前が変更されます:xDEVICE = $(df "$ {xFILE}" | tail -1l | awk '{print $ 6} ')
kbulgrien 14年

各メンバーを「マスター」として繰り返すのではなく、ハードリンクのグループだけが必要な場合は、を使用しますfind ... -xdev -type f -links +1 -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate。これはfsを一度しか通過しないため、非常に高速です。一度に複数のFSを使用する場合は、iノード番号の前にFS idを付ける必要があります。たぶんfind -exec stat... -printf ...
ピーター・コーデス

その考えを答えに変えた
ピーター・コーデス

2

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 "";

このスクリプトに関するコメントを別の回答として投稿しました。
ダニエルアンダーソン

1

GUIソリューションはあなたの質問に本当に近づきます:

以前のコメンテーターが指摘したように、ファイル「名前」は同じデータへの単なるエイリアスであるため、「ls」から実際のハードリンクファイルをリストすることはできません。ただし、実際には、Linuxで同じデータ(ハードリンクとして)を指すファイル名のパスリストを表示するという、ほぼ望みどおりのGUIツールがあります。これはFSLintと呼ばれます。必要なオプションは、[名前の衝突]の下にあります->検索(XX)で[チェックボックス$ PATH]の選択を解除し、ドロップダウンボックスから[for ...]の後の上部中央に向かって[エイリアス]を選択します。

FSLintのドキュメントは非常に貧弱ですが、[検索パス]の下にある[限定]ディレクトリツリーを[再帰]のチェックボックスをオンにして確認することがわかりました。前述のオプション、同じデータを「指す」パスと名前を持つハードリンクされたデータのリストは、プログラムの検索後に生成されます。


FSlintはで見つけることができpixelbeat.org/fslint
mklement0

1

ls「エイリアス」を使用してハードリンクを強調表示するように構成できますが、前に述べたように、ハードリンクの「ソース」を表示する方法はありません.hardlink

ハードリンクを強調表示する

あなたのどこかに以下を追加してください .bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.