走る
ln /a/A /b/B
ファイルAがa
で指しているフォルダls
を見てみたい。
あなたのファイルのinode番号を見つけることができます
ls -i
そして
ls -l
参照カウント(特定のiノードへのハードリンク数)を表示します
iノード番号が見つかったら、同じiノードを持つすべてのファイルを検索できます。
find . -inum NUM
現在のディレクトリ(。)内のinode NUMのファイル名を表示します。
あなたの質問に対する明確な答えは実際にはありません。シンボリックリンクとは異なり、ハードリンクは「元のファイル」と区別がつきません。
ディレクトリエントリは、ファイル名とiノードへのポインタで構成されています。 iノードには、ファイルのメタデータと(実際のファイルの内容へのポインタ)が含まれています。ハードリンクを作成すると、別のファイル名+同じiノードへの参照が作成されます。これらの参照は単方向です(少なくとも典型的なファイルシステムでは) - iノードは参照カウントを保持するだけです。 「元の」ファイル名がどれであるかを見つけるための固有の方法はありません。
ところで、ファイルを "削除する"ためのシステムコールがunlink
と呼ばれるのはこのためです。ハードリンクを削除するだけです。 iノードの参照カウントが0になった場合にのみ、添付データのiノードが削除されます。
特定のiノードへの他の参照を見つける唯一の方法は、問題のiノードを参照しているファイルを確認しながら、ファイルシステムを徹底的に検索することです。このチェックを実行するには、シェルから 'test A -ef B'を使用します。
UNIXにはハードリンクとシンボリックリンクがあります(それぞれ"ln"
と"ln -s"
で作られています)。シンボリックリンクは、単に別のファイルへの実際のパスを含み、ファイルシステムを横断することができるファイルです。
ハードリンクはUNIXの初期の頃からありました(とにかく覚えていることができます、そしてそれはかなりしばらく前に戻ってきています)。これらは、exactと同じ基本データを参照する2つのディレクトリエントリです。ファイル内のデータは、そのinode
によって指定されます。ファイルシステム上の各ファイルはiノードを指していますが、各ファイルが固有のiノードを指している必要はありません。ここからハードリンクが発生します。
Iノードは特定のファイルシステムに対してのみ一意であるため、ハードリンクは同じファイルシステム上になければならないという制限があります(シンボリックリンクとは異なり)。シンボリックリンクとは異なり、特権ファイルはありません。それらはすべて同等です。データ領域はallそのinodeを使用しているファイルが削除されたときにのみ解放されます(そしてすべてのプロセスがそれを閉じますが、これは別の問題です)。
特定のファイルのiノードを取得するには、"ls -i"
コマンドを使用できます。それから"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
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
次の簡単なものはどうですか。 (後に、上記の長いスクリプトが置き換えられる可能性があります。)
特定のファイル<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 {} \;
ファイルシステム内のすべてのハードリンクを見つけるためのスクリプトに関する回答はたくさんあります。それらのほとんどは、ファイルシステム全体を-samefile
でスキャンしてEACH多重リンクファイルを探すようなばかげたことをします。狂ってる;必要なのは、iノード番号をソートして重複を印刷することだけです。
find directories.. -xdev ! -type d -links +1 -printf '%20D %20i %p\n' | sort -n | uniq -w 42 --all-repeated=separate
(私のオリジナルのコマンドを微調整して、FS-id(%D
)をサポートし、通常のファイルだけでなくすべてのディレクトリではないファイルタイプを処理できるようにしてくれた。@Tinoに感謝します。)
! -type d -links +1
を使うことは、sortの入力がuniqの最終出力と同じくらい大きいことを意味します。ハードリンクのセットの1つだけを含むサブディレクトリでそれを実行しない限り。とにかく、これは他の投稿されたソリューションよりもファイルシステムを再トラバースするLOTより少ない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
は非常に限られたフィールド選択のサポートを持っているので、私はfind出力を埋め込み、固定幅を使います。 20文字は、可能な最大iノード番号またはデバイス番号(2 ^ 64-1 = 18446744073709551615)に十分な幅です。 XFSは、ディスク上の割り当て位置に基づいて、0から連続していないiノード番号を選択するため、大規模なXFSファイルシステムでは、数十億のファイルがなくても32ビットを超えるiノード番号を使用できます。他のファイルシステムは、たとえ巨大でなくても、20桁のiノード番号を持つことがあります。
TODO:パスごとに重複グループをソートします。マウントポイントでソートしてから、inode番号を使うと、ハードリンクがたくさんあるサブディレクトリがいくつかある場合は、混在させることができます。 (すなわち、重複グループのグループは一緒になるが、出力はそれらを混同する)。
最後のsort -k 3
は、行のグループを単一のレコードとしてではなく、行を個別にソートします。改行のペアをNULバイトに変換するための何かを前処理し、GNU sort --zero-terminated -k 3
を使用することでうまくいく可能性があります。ただし、tr
は1文字のみで動作し、2 - > 1または1 - > 2パターンではありません。 Perl
はそれをするでしょう(あるいは単にPerlやawkの中でパースしソートするでしょう)。 sed
も動作するかもしれません。
これは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
私は比較を容易にするためにできるだけそれをあなたのものに似せておくようにしました。
Globが十分であれば、$IFS
マジックを常に避けるべきです。なぜなら、globは不必要に複雑になり、ファイル名は実際には改行を含むことができるからです(しかし実際には主に最初の理由)。
ls
とそのような出力を手動で解析することはできるだけ遅らせるべきです。遅かれ早かれ噛み付くでしょう。たとえば、最初のawk
行では、スペースを含むすべてのファイル名に失敗します。
printf
は%s
構文と非常に堅牢であるため、結局のところ問題を解決するでしょう。また、echo
とは異なり、出力を完全に制御でき、すべてのシステムで一貫しています。
この場合stat
はあなたに多くのロジックを節約することができます。
GNU find
は強力です。
あなたのhead
とtail
の呼び出しはawk
の中で直接処理されたかもしれません。 exit
コマンドおよび/またはNR
変数の選択これはプロセス呼び出しを節約するでしょう、そしてそれはほとんどいつも勤勉なスクリプトでパフォーマンスを著しく向上させます。
あなたのegrep
はちょうどgrep
でも構いません。
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ソリューションは本当にあなたの質問に近づきます:
以前のコメンテーターが指摘したように、ファイル "names"は同じデータへの単なるエイリアスであるため、 "ls"から実際にハードリンクされたファイルをリストすることはできません。しかし、実際にはLinuxツールの下で同じデータを(ハードリンクとして)指すファイル名のパスリストを表示することが目的のものに本当に近づくGUIツールがあります。これはFSLintと呼ばれます。必要なオプションは、検索(XX) - > [名前の衝突] - > [チェックボックス$ PATH]の選択を解除し、[for ...]の後の上部中央に向かって[Aliases]を選択します。
FSLintはあまり文書化されていませんが、[検索パス]の下の限定されたディレクトリツリーで[再帰]チェックボックスがオンになっていることを確認しました。前述のオプションでは、プログラムが検索した後に、同じデータを「指す」パスと名前を持つハードリンクされたデータのリストが作成されます。