首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过shell脚本查找*非*硬链接或硬链接目录下的文件

通过shell脚本查找*非*硬链接或硬链接目录下的文件
EN

Stack Overflow用户
提问于 2022-06-13 16:28:08
回答 3查看 90关注 0票数 1

我想找到所有的文件,不是硬链接或硬链接目录下。我找到了这个太棒了,但是下面的命令不处理硬链接目录下的情况!

代码语言:javascript
复制
find /1 -type f -links 1 -print

例如:

代码语言:javascript
复制
/1/2/3/test.txt
/1/A/3/test.txt

2是硬链接到A,那么我们只期望找到一个test.txt文件。

android的另一个例子是:

代码语言:javascript
复制
$ adb shell ls -li /data/data/com.android.nfc |grep files
4243 drwxrwx--x 2 nfc  nfc  3488 2022-06-13 11:08 files
$ adb shell ls -li /data/user/0/com.android.nfc |grep files
4243 drwxrwx--x 2 nfc  nfc  3488 2022-06-13 11:08 files
$ adb shell ls -li /data/data/com.android.nfc/files/service_state.xml
5877 -rw------- 1 nfc nfc 100 2022-06-13 11:08 /data/data/com.android.nfc/files/service_state.xml
$ adb shell ls -li /data/user/0/com.android.nfc/files/service_state.xml
5877 -rw------- 1 nfc nfc 100 2022-06-13 11:08 /data/user/0/com.android.nfc/files/service_state.xml
EN

回答 3

Stack Overflow用户

发布于 2022-06-13 22:19:19

支持指向目录的无限制硬链接的系统很少,但是可以使用绑定挂载创建类似的情况。(见什么是绑定坐骑?.)

尝试使用此谢尔查克-clean代码列出当前目录下没有多个路径的文件(由绑定挂载或指向目录的链接引起):

代码语言:javascript
复制
#! /bin/bash -p

shopt -s lastpipe

declare -A devino_of_file
declare -A count_of_devino
find . -type f -printf '%D.%i-%p\0' \
    |   while IFS= read -r -d '' devino_path; do
            devino=${devino_path%%-*}
            path=${devino_path#*-}
            devino_of_file[$path]=$devino
            count_of_devino[$devino]=$(( ${count_of_devino[$devino]-0}+1 ))
        done

for path in "${!devino_of_file[@]}"; do
    devino=${devino_of_file[$path]}
    (( ${count_of_devino[$devino]} == 1 )) && printf '%s\n' "$path"
done
  • shopt -s lastpipe确保管道中的while循环中设置的变量在管道完成后保持不变。它需要Bash 4.2 (2011年发布)或更高版本。
  • 代码使用"devino“值。路径的devino值由路径的设备号和inode号组成,由一个.字符分隔。devino字符串应该唯一地标识系统上的文件,而不依赖于它的任何路径。
  • devino_of_file关联数组将路径映射到相应的devino值。
  • count_of_devino关联数组将devino字符串映射到它们找到的路径数。
  • 有关BashFAQ/001 (如何读取文件(数据流、变量)逐行(和/或逐字段)?)的解释,请参阅while IFS= read -r -d '' ...
  • 当目录树中的所有文件都已被处理后,所有其devino值为1的路径(意味着没有找到到同一文件的其他路径)都会被打印出来。
  • 填充关联数组的代码可以处理任意路径(包括包含空格或换行符的路径),但是如果任何路径包含换行符(因为'%s\n'格式字符串),输出将是无用的。
  • 自动避免由符号链接引起的替代路径,因为默认情况下find不遵循符号链接。但是,如果使用-follow选项到find,代码仍然可以工作。(使用符号链接测试比使用目录硬链接或绑定挂载更容易。)

注意,Bash代码运行非常慢。它是以一种非常艰苦的方式解释的。如果正在处理的目录树有大量文件,那么上面的代码可能会太慢。例如,它在我的测试VM上以每秒大约10,000的速度处理文件。

票数 2
EN

Stack Overflow用户

发布于 2022-06-13 18:01:31

请原谅评论中的幽默,但我认为你不明白你的问题。

我的意思是,当您创建一个文件时,它就是一个链接

代码语言:javascript
复制
$: date > file1
$: ls -l file1 # note the 2nd field - the "number of hard links"
-rw-r--r--. 1 P2759474 518 29 Jun 13 17:34 file1

您认为file1是文件,但它是...complicated,lol。

上面的date命令创建输出。重定向告诉“系统”您想要“文件”中的数据,因此它分配磁盘上的空间,将数据写入该空间,并创建定义“文件”的inode

“硬链接”基本上就是指向该数据的链接。如果您创建了另一个链接,那么它是同一个带有其他名称的“文件”。编辑两者都是(如果你做了几个编辑),因为它们是同一个文件。

代码语言:javascript
复制
$: date >file1
$: ln file1 file2
$: diff file?
$: cat file1
Mon Jun 13 17:30:22 GMT 2022
$: date >file2
$: diff file?
$: cat file1
Mon Jun 13 17:31:06 GMT 2022

现在,符号链接是另一种具有不同inode的文件,包含它“链接”到的文件的名称,但是硬链接是文件。实际上,ls -i将向您显示inode索引号。

代码语言:javascript
复制
$: date >file1
$: ln file1 file2
$: diff file?
$: cat file2
Mon Jun 13 17:34:41 GMT 2022
$: ls -li file? # note the 1st and 3rd fields
24415801 -rw-r--r--. 2 paul 518 29 Jun 13 17:34 file1
24415801 -rw-r--r--. 2 paul 518 29 Jun 13 17:34 file2
$: rm file2
$: ls -li file? # note the 1st and 3rd fields
24415801 -rw-r--r--. 1 P2759474 518 29 Jun 13 17:34 file1

让我们用这个名称创建一个不同的文件,然后再进行比较。

代码语言:javascript
复制
$: date >file2
$: cat file? # not linked now
Mon Jun 13 17:34:41 GMT 2022
Mon Jun 13 17:41:23 GMT 2022
$: diff file? # now they differ
1c1
< Mon Jun 13 17:34:41 GMT 2022
---
> Mon Jun 13 17:41:23 GMT 2022
$: ls -li file? # and have different inodes, one link each
24415801 -rw-r--r--. 1 P2759474 518 29 Jun 13 17:34 file1
24419687 -rw-r--r--. 1 P2759474 518 29 Jun 13 17:41 file2

如果我cad复制了原始数据,diff将是空的,但是它仍然是一个不同的inode,所以有一个不同的文件,并且我可以独立地编辑它们。

还有一个符号连接-

代码语言:javascript
复制
$: ln -s file1 file3
$: diff file1 file3
$: ls -li file?
24415801 -rw-r--r--. 1 P2759474 518 29 Jun 13 17:34 file1
24419687 -rw-r--r--. 1 P2759474 518 29 Jun 13 17:41 file2
24419696 lrwxrwxrwx. 1 P2759474 518  5 Jun 13 17:44 file3 -> file1

打开符号链接通常会打开它的目标文件,但它可能取决于您使用的是什么工具.注意不同之处

您不能在单独的文件系统上创建指向文件的硬链接,因为它不是这样工作的。你可以用一个符号链接。

你可能要找的是

代码语言:javascript
复制
for f in *; [[ -f "$f" ]] && echo "$f"; done

或者类似的东西。

希望这能有所帮助。

票数 0
EN

Stack Overflow用户

发布于 2022-06-13 20:25:58

从以前对这个答案的编辑的评论来看,复制似乎是因为一些文件由于绑定挂载而出现在文件系统中的两个不同位置。

在这种情况下,您使用的原始代码会产生技术上正确的输出。但是,它不止一次地列出了一些相关文件(因为它们有多个名称):

代码语言:javascript
复制
find /1 -type f -links 1 -print

安装的文件系统由其设备编号唯一标识。文件在该文件系统中通过其inode编号进行唯一标识。因此,文件可以由(device#,inode#)元组在特定主机上唯一标识。find可以提供这些元组和文件名,如@pjh的回答所示:

代码语言:javascript
复制
find /1 -type f -links 1 -printf '%D.%i %p\0'

一个简单的(GNU) awk脚本可以过滤输出,以便只列出每个唯一的路径(device#,inode#):

代码语言:javascript
复制
find /1 -type f -links 1 -printf '%D.%i %p\0' |
gawk -v RS='\0' '!id[$1]++ && sub(/^[0-9.]+ /,"")'

这使用了常见的awk成语!x[y]++,只有当元素y被插入数组x时才会计算为true (它是用值0插入的,第一次看到y,然后值就增加了;!0是真的)。(device#,inode#)前缀被sub()删除。如果“模式”的计算结果为true,awk将隐式打印已处理的记录。即。当第一次看到(device#,inode#)元组并成功地删除前缀时。(GNU) find输出由null而不是换行符分隔,因此(GNU) awk脚本也将输入记录分隔符RS设置为null。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72606124

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档