2009-07-25 09:41:26 +0000 2009-07-25 09:41:26 +0000
104
104

如何通过ls看到实际的硬链接?

我运行

ln /a/A /b/B

我想在文件夹a看到A文件通过ls指向的地方。

答案 (9)

182
182
182
2009-07-25 10:01:32 +0000

你可以用

ls -i

找到你的文件的inode号,

ls -l

显示引用数(指向某个inode的硬链接数)

找到inode号后,你可以搜索所有具有相同inode的文件。

find . -inum NUM

将显示当前目录(.)中inode NUM的文件名。

66
66
66
2009-07-25 09:51:57 +0000

对于你的问题,其实并没有一个明确的答案。与符号链接不同,硬链接与 “原始文件 "是无法区分的。

目录条目由一个文件名和一个指向inode的指针组成。inode又包含文件元数据和(指向)实际文件内容的指针)。创建一个硬链接会创建另一个文件名+指向同一个inode的引用。这些引用是单向的(至少在典型的文件系统中)–inode只保留一个引用计数。没有内在的方法来找出哪个是 "原始 "文件名。

顺便说一下,这就是为什么系统调用 "删除 "一个文件叫做unlink。它只是删除了一个硬链接。

只有当inode的引用数降到0时,才会删除inode和附加的数据。

要找到一个给定inode的其他引用,唯一的方法是在文件系统中彻底搜索,检查哪些文件引用了相关的inode。你可以在shell中使用'test A -ef B'来执行这个检查。

24
24
24
2009-07-25 10:01:38 +0000
ls -l

第一列将代表权限。第二列将是子项目的数量(对于目录)或通往文件的同一数据(硬链接,包括原始文件)的路径数量。Eg:

-rw-r--r--@ 2 [username] [group] [timestamp] HardLink
-rw-r--r--@ 2 [username] [group] [timestamp] Original
               ^ Number of hard links to the data
``` 。
14
14
14
2013-08-15 08:52:16 +0000

下面这个更简单的怎么样?后面可能会取代上面的长脚本!)

如果你有一个特定的文件<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 {} \;
6
6
6
2015-04-21 19:32:47 +0000

有很多答案都是用脚本来查找文件系统中的所有硬链接。他们中的大多数人都会做一些愚蠢的事情,比如运行find来扫描整个文件系统中每个多重链接文件的-samefile。这太疯狂了;你所需要的只是根据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 在前42列上进行数字排序和uniquify,用空行分隔组

使用sort -n | uniq ...意味着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?: 用! -type d -links +1awk来解除输出垫。cut对字段选择的支持非常有限,所以我垫上查找输出,使用固定宽度。20chars的宽度足以满足最大可能的inode或设备号(2^64-1=18446744073709551615)。XFS根据它们在磁盘上分配的位置来选择inode号,而不是从0开始连续地选择,所以大型的XFS文件系统可以有>32位的inode号,即使它们没有数十亿个文件。其他文件系统即使不是巨大的,也可能有20位的inode号。

TODO: 按路径对重复的组进行排序。如果你有几个不同的子目录,其中有很多硬链接,那么按挂载点和inode号排序就会把事情混在一起。(也就是说,重复组的群组会在一起,但输出会把它们混在一起)。

最后的uniq会分别对行进行排序,而不是将行的组作为一条记录。用一些东西进行预处理,将一对换行符转化为NUL字节,并使用GNU sort -k 3可能会起到作用。sort --zero-terminated -k 3只对单个字符进行操作,而不是2-/>1或1-/>2的模式。tr可以做到(或者在perl或awk中进行解析和排序)。perl也可能会成功。

3
3
3
2012-06-13 07:40:43 +0000

重写了你的脚本,用更直接的方法来查找信息,因此减少了很多进程调用。

#!/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 魔法,因为它是不必要的复杂,而且文件名实际上可以包含换行符 (但实际上主要是第一个原因)。

  • 你应该尽量避免手动解析ls之类的输出,因为它迟早会咬你。例如:在你的第一行awk中,你对所有包含空格的文件名都会失败。

-printf往往会在最后省去麻烦,因为它与%s语法是如此健壮。它还能让你完全控制输出,并且在所有系统中都是一致的,不像 echo

  • stat在这种情况下可以为你节省很多逻辑。

  • GNU find功能强大。

  • 你的headtail调用可以直接在awk中用exit命令和/或选择NR变量来处理。这样可以节省进程调用,这对于工作繁忙的脚本来说,几乎总是能提高性能。

  • 你的 egreps 也可以只是 grep

2
2
2
2011-11-16 22:46:38 +0000

基于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
1
1
2015-01-20 18:00:05 +0000

一个GUI解决方案非常接近你的问题。

你不能从 “ls "中列出真正的硬链接文件 因为正如前面的评论者指出的那样 文件名只是同一数据的别称 然而,实际上有一个GUI工具可以非常接近你的需求,那就是在linux下显示指向相同数据(作为硬链接)的文件名的路径列表,它叫做FSLint。你想要的选项是在 "名称冲突 "下 -/> 取消选择 "搜索(XX) "中的 "复选框$PATH” -/> 从 “for… "后的下拉框中选择 "别名"。

FSLint的文档很不完善,但我发现,确保 "搜索路径 "下的有限目录树,选中 "Recurse? "的复选框和上述选项,程序搜索后会产生一个路径和名称 "指向 "同一数据的硬链接数据列表。

1
1
1
2017-12-06 17:34:25 +0000

你可以配置ls使用 “别名 "来突出显示硬链接,但如前所述,没有办法显示硬链接的 "来源",这就是为什么我附加.hardlink来帮助你的原因。

在你的.bashrc

alias ll='LC_COLLATE=C LS_COLORS="$LS_COLORS:mh=1;37" ls -lA --si --group-directories-first'
```中添加以下内容。