走る
ln /a/A /b/B
a
ファイルAが指すフォルダーを参照したいのですがls
。
走る
ln /a/A /b/B
a
ファイルAが指すフォルダーを参照したいのですがls
。
回答:
ファイルのinode番号を見つけるには
ls -i
そして
ls -l
参照カウント(特定のiノードへのハードリンクの数)を表示します
iノード番号を見つけたら、同じiノードを持つすべてのファイルを検索できます。
find . -inum NUM
現在のディレクトリ(。)のiノードNUMのファイル名を表示します
あなたの質問に対する明確な答えは実際にはありません。シンボリックリンクとは異なり、ハードリンクは「元のファイル」と区別できません。
ディレクトリエントリは、ファイル名とiノードへのポインタで構成されます。iノードには、ファイルのメタデータと(実際のファイルの内容へのポインター)が含まれます。ハードリンクを作成すると、別のファイル名+同じiノードへの参照が作成されます。これらの参照は単方向です(少なくとも典型的なファイルシステムでは)-iノードは参照カウントのみを保持します。「元の」ファイル名を見つけるための固有の方法はありません。
ところで、これが、ファイルを「削除」するシステムコールが呼び出される理由unlink
です。ハードリンクを削除するだけです。接続されたデータのiノードは、iノードの参照カウントが0になった場合にのみ削除されます。
特定のiノードへの他の参照を見つける唯一の方法は、ファイルシステムを徹底的に検索して、問題のiノードを参照しているファイルを確認することです。このチェックを実行するには、シェルから「test A -ef B」を使用できます。
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
INUM=$(stat -c %i $1)
。またNUM_LINKS=$(stat -c %h $1)
。man stat
使用できるその他の形式変数を参照してください。
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
inode
ディスクコンテンツを指すだけであるという点で、どちらも同じです。
次の簡単なものはどうですか?(後者は上の長いスクリプトを置き換えるかもしれません!)
特定のファイルが<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
、ファイルもディレクトリになる可能性があるため、使用しません。
.
および..
エントリはハードリンクです。ディレクトリ内のサブディレクトリの数は、のリンクカウントから知ることができます.
。find -samefile .
それでもsubdir/..
出力は出力されないため、これはとにかく意味がありません。 find
(少なくともGNUバージョン)は..
、でも無視するようにハードコードされているよう-noleaf
です。
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ノード番号を持っている場合は注意してください)
ファイルシステム内のすべてのハードリンクを見つけるためのスクリプトには多くの答えがあります。それらのほとんどは、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 /bar
1つのファイルに最適です。
-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
うまくいくかもしれません。
%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ビットシステムがある限り保持される可能性があります。
! -type d
代わりにを使用することの素晴らしい点-type f
。ファイルのコレクションを整理することで、ハードリンクされたシンボリックリンクがファイルシステムにあります。改善されたバージョンで私の回答を更新しました(ただし、fs-idを最初に置いたので、ソート順は少なくともファイルシステムごとにグループ化します。)
これは、トロコロ・マッチョ自身の回答とスクリプトに対するコメントですが、コメントボックスには収まりません。
情報を見つけるためのより簡単な方法でスクリプトを書き直し、プロセス呼び出しを大幅に減らします。
#!/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
強力です。
あなたhead
とtail
呼び出しがで直接処理されている可能性awk
などでexit
コマンドおよび/または上で選択するNR
変数。これにより、プロセス呼び出しが保存され、ほとんどの場合、勤勉なスクリプトのパフォーマンスが大幅に向上します。
あなたegrep
のものはちょうど同様であるかもしれませんgrep
。
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 ...
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 "";
GUIソリューションはあなたの質問に本当に近づきます:
以前のコメンテーターが指摘したように、ファイル「名前」は同じデータへの単なるエイリアスであるため、「ls」から実際のハードリンクファイルをリストすることはできません。ただし、実際には、Linuxで同じデータ(ハードリンクとして)を指すファイル名のパスリストを表示するという、ほぼ望みどおりのGUIツールがあります。これはFSLintと呼ばれます。必要なオプションは、[名前の衝突]の下にあります->検索(XX)で[チェックボックス$ PATH]の選択を解除し、ドロップダウンボックスから[for ...]の後の上部中央に向かって[エイリアス]を選択します。
FSLintのドキュメントは非常に貧弱ですが、[検索パス]の下にある[限定]ディレクトリツリーを[再帰]のチェックボックスをオンにして確認することがわかりました。前述のオプション、同じデータを「指す」パスと名前を持つハードリンクされたデータのリストは、プログラムの検索後に生成されます。
ls
「エイリアス」を使用してハードリンクを強調表示するように構成できますが、前に述べたように、ハードリンクの「ソース」を表示する方法はありません.hardlink
。
あなたのどこかに以下を追加してください .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
link(2)
システムコールの後、1つがオリジナルで1つがリンクであるという意味はありません。これが、答えが指摘しているように、すべてのリンクを見つける唯一の方法がである理由ですfind / -samefile /a/A
。iノードの1つのディレクトリエントリは、同じiノードの他のディレクトリエントリを「認識」していないためです。彼らがするのは、iノードを参照カウントするだけですunlink(2)ed
。(これはls
出力の「リンクカウント」です)。