如何通过Shell脚本查找非硬链接且不在硬链接目录下的文件
问题根源
原命令find /1 -type f -links 1 -print仅对文件自身的硬链接计数做校验,完全没有处理路径上父目录为重复inode的场景——这类场景不仅包括目录硬链接,也包括安卓系统中大量存在的绑定挂载(比如/data/data与/data/user/0就是典型的同inode绑定挂载路径)。同个文件会通过多个重复目录路径被扫描到,自然会出现重复输出。
实现逻辑
要满足需求只需要守住两个过滤规则:
- 目标文件自身硬链接计数为1,即文件本身没有其他硬链接别名
- 所有同唯一标识(设备号+inode号,避免跨文件系统inode重复)的目录、文件仅处理一次,从根源避免重复进入重复目录、重复输出同inode文件
可用脚本
安卓环境适配版(兼容POSIX shell、adb shell,无需bash依赖)
# 替换为实际要搜索的根路径 SEARCH_ROOT="/data" # 临时文件存储已处理的「设备号:inode号」唯一标识 TMP_MARK=$(mktemp) trap 'rm -f "$TMP_MARK"' EXIT find "$SEARCH_ROOT" -exec stat -c '%d:%i %F %n' {} \; | while read -r unique_id ftype path; do # 该inode已处理过,直接跳过 if grep -qxF "$unique_id" "$TMP_MARK"; then continue fi # 标记为已处理 echo "$unique_id" >> "$TMP_MARK" # 仅输出普通文件、且硬链接数为1的条目 if [ "$ftype" = "regular file" ] && [ ! -L "$path" ]; then link_cnt=$(stat -c '%h' "$path") if [ "$link_cnt" -eq 1 ]; then echo "$path" fi fi done
普通Linux Bash版(性能更好)
#!/bin/bash SEARCH_ROOT="/1" declare -A seen # 格式:设备号 inode 类型 路径 find "$SEARCH_ROOT" -printf '%D %i %y %p\n' | while read -r dev inode ftype path; do unique_id="${dev}:${inode}" if [[ -n "${seen[$unique_id]}" ]]; then continue fi seen[$unique_id]=1 if [[ "$ftype" == "f" ]]; then link_cnt=$(stat -c '%h' "$path") if [[ "$link_cnt" -eq 1 ]]; then echo "$path" fi fi done
效果说明
- 遍历过程中首次遇到某个inode的目录/文件时就会做标记,后续再遇到同唯一标识的条目(不管是另一个路径的硬链接目录、绑定挂载目录,还是同个文件的其他硬链接路径)都会直接跳过,不会重复处理
- 针对给出的安卓NFC目录示例,最终只会输出1次
service_state.xml的路径,不会重复 - 脚本默认输出先遍历到的路径,如果需要固定输出某个路径前缀下的结果,可以调整搜索根路径的顺序,把优先要输出的路径放在前面遍历
内容的提问来源于stack exchange,提问作者lucky1928
相关产品推荐
相关产品推荐

