我跑
ln /a/A /b/B
我想a
在文件A指向by的文件夹中看到ls
。
我跑
ln /a/A /b/B
我想a
在文件A指向by的文件夹中看到ls
。
Answers:
您可以使用以下命令找到文件的索引节点号
ls -i
和
ls -l
显示引用计数(到特定inode的硬链接数)
找到索引节点编号后,可以搜索具有相同索引节点的所有文件:
find . -inum NUM
将在当前目录(。)中显示inode NUM的文件名
您的问题确实没有明确的答案。与符号链接不同,硬链接与“原始文件”没有区别。
目录条目由文件名和指向索引节点的指针组成。索引节点又包含文件元数据和(指向实际文件内容的指针)。创建硬链接会创建另一个文件名+对相同inode的引用。这些引用是单向的(至少在典型的文件系统中)-索引节点仅保留引用计数。没有固有的方法可以找出哪个是“原始”文件名。
顺便说一下,这就是为什么系统调用“删除”文件的原因unlink
。它只是删除一个硬链接。仅当索引节点的引用计数降至0时,索引节点的附加数据才会被删除。
查找对给定索引节点的其他引用的唯一方法是彻底搜索文件系统,检查哪些文件引用了相关索引节点。您可以从外壳程序中使用“ test A -ef B”来执行此检查。
UNIX具有硬链接和符号链接(分别由"ln"
和制成"ln -s"
)。符号链接只是一个文件,其中包含另一个文件的真实路径,并且可以跨文件系统。
硬链接自UNIX成立以来就存在(无论如何,我仍然记得,这已经有一段时间了)。它们是两个引用完全相同的基础数据的目录条目。文件中的数据由其指定inode
。文件系统上的每个文件都指向一个索引节点,但不要求每个文件都指向一个唯一的索引节点-这就是硬链接的来源。
由于索引节点仅对于给定的文件系统是唯一的,因此存在一个限制,即硬链接必须位于同一文件系统上(与符号链接不同)。请注意,与符号链接不同,没有特权文件-它们都是相等的。仅当删除使用该inode的所有文件时(所有进程也将其关闭),才释放数据区域。
您可以使用该"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
INUM=$(stat -c %i $1)
。也NUM_LINKS=$(stat -c %h $1)
。请参阅man stat
以获取更多可以使用的格式变量。
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
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
对一组硬链接文件的每个成员运行一次。 find ... -printf '%16i %p\n' | sort -n | uniq -w 16 --all-repeated=separate
会起作用,(16的宽度不足以表示2 ^ 63-1的十进制表示,因此,当您的XFS文件系统足够大以使inode数很高时,请
脚本有很多答案,可以找到文件系统中的所有硬链接。他们中的大多数人都做一些愚蠢的事情,例如运行find来扫描整个文件系统以-samefile
查找EACH多重链接文件。这太疯狂了; 您所需要做的就是对inode号进行排序并打印重复项。
只需一次通过文件系统即可查找和分组所有硬链接文件集
find dirs -xdev \! -type d -links +1 -printf '%20D %20i %p\n' |
sort -n | uniq -w 42 --all-repeated=separate
这比查找多组硬链接文件的其他答案要快得多。
find /foo -samefile /bar
仅需一个文件就非常好。
-xdev
:限制为一个文件系统。由于我们还将FS-id打印到uniq上,因此并非严格需要! -type d
拒绝目录:.
和..
条目表示它们始终处于链接状态。-links +1
:链接数严格 > 1
-printf ...
显示FS-id,inode编号和路径。(通过填充到固定的列宽,我们可以知道uniq
。)sort -n | uniq ...
在前42列上进行数字排序和唯一化,用空行分隔组使用! -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 ?:用awk
或取消输出的填充cut
。 uniq
对字段选择的支持非常有限,因此我填充了find输出并使用了固定宽度。20个字符的宽度足以容纳最大可能的inode或设备编号(2 ^ 64-1 = 18446744073709551615)。XFS根据在磁盘上分配的位置来选择索引节点号,而不是从0开始连续地选择索引索引号,因此,大型XFS文件系统即使没有数十亿个文件,也可以具有大于32位的索引节点号。其他文件系统可能不具有20位的inode编号,即使它们不是巨大的。
TODO:按路径对重复项进行排序。如果您有几个带有大量硬链接的不同子目录,则按挂载点对它们进行排序,然后按索引号将它们混合在一起。(即dup-group组在一起,但是输出将它们混合在一起)。
final sort -k 3
将单独对行进行排序,而不是将行组作为单个记录进行排序。进行一些预处理以将一对换行符转换为NUL字节,然后使用GNU sort --zero-terminated -k 3
可能会成功。 tr
但是,仅可用于单个字符,而不能用于2-> 1或1-> 2模式。 perl
会做到这一点(或者只是在perl或awk中解析和排序)。 sed
可能也可以。
%D
是文件系统标识符(这是当前引导独特而没有文件系统被umount
ED),所以下面是更通用的: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放在了首位,所以排序顺序至少是按文件系统分组。)
这是对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
变量上进行选择来处理。这将节省进程调用,这几乎总是可以在辛苦的脚本中大大提高性能。
你egrep
的也可能只是grep
。
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 ...
基于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”中列出实际的硬链接文件,因为如先前的评论员所指出的那样,文件“名称”仅是相同数据的别名。但是,实际上有一个GUI工具非常接近您想要的,即显示文件名的路径列表,该文件名指向Linux下的相同数据(如硬链接),称为FSLint。您想要的选项位于“名称冲突”下->在搜索(XX)中取消选择“复选框$ PATH”->并从下拉框中选择“别名”,然后指向顶部。
FSLint的文档记录很少,但是我发现要确保“搜索路径”下的受限目录树已选中“递归?”复选框。以及上述选项,在程序搜索后,将产生具有“指向”相同数据的路径和名称的硬链接数据列表。
您可以配置ls
为使用“别名”突出显示硬链接,但是如前所述,没有办法显示硬链接的“源”,这就是我在后面附加.hardlink
帮助的原因。
将以下内容添加到您的 .bashrc
alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
link(2)
系统调用之后,没有任何意义是原始的而链接是链接。正如答案所指出的,这就是为什么查找所有链接的唯一方法是find / -samefile /a/A
。因为一个索引节点的一个目录条目不会“知道”同一索引节点的其他目录条目。他们所做的只是引用索引节点,以便当其姓氏为时可以将其删除unlink(2)ed
。(这是ls
输出中的“链接计数” )。