返回列表 发布新帖

[用户教程] 脚本管理硬链接,删除or补全orkeep

256 0
发表于 2026-3-5 11:36:02 | 查看全部 阅读模式 IP:–浙江–绍兴

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
如果创建硬链接后,删掉了一端,那么如何删除另一端,或者如何补全另一端呢?

写了个脚本,实测ok。
  1. #!/bin/bash
  2. set -e

  3. DEFAULT_LIST="orphan_list.txt"

  4. prompt_dir () {
  5.   local label="$1"
  6.   local def="$2"
  7.   local v=""
  8.   while true; do
  9.     read -p "$label [$def]: " v
  10.     v="${v:-$def}"
  11.     if [ -d "$v" ]; then
  12.       echo "$v"
  13.       return 0
  14.     else
  15.       echo "Not a directory: $v"
  16.     fi
  17.   done
  18. }

  19. prompt_file () {
  20.   local label="$1"
  21.   local def="$2"
  22.   local v=""
  23.   while true; do
  24.     read -p "$label [$def]: " v
  25.     v="${v:-$def}"
  26.     if [ -f "$v" ]; then
  27.       echo "$v"
  28.       return 0
  29.     else
  30.       echo "Not a file: $v"
  31.     fi
  32.   done
  33. }

  34. scan_orphans () {
  35.   local ABC="$1"
  36.   local XYZ="$2"
  37.   local OUT="$3"

  38.   echo "Scanning XYZ for candidates (files with links=1) and filtering those whose inode is absent in ABC..."
  39.   : > "$OUT"

  40.   # 注意:这版对每个候选都会在 ABC 内 find 一次 inode,文件多会慢,但最直观易用。
  41.   find "$XYZ" -type f -links 1 | while IFS= read -r f; do
  42.     inode=$(stat -c %i "$f") || continue
  43.     if ! find "$ABC" -inum "$inode" -print -quit 2>/dev/null | grep -q .; then
  44.       echo "$f" >> "$OUT"
  45.     fi
  46.   done

  47.   echo "Scan done. List saved to: $OUT"
  48.   echo -n "Total lines: "
  49.   wc -l "$OUT" | awk '{print $1}'
  50. }

  51. view_list () {
  52.   local LIST="$1"
  53.   echo "---- First 50 lines of $LIST ----"
  54.   nl -ba "$LIST" | head -n 50
  55.   echo "--------------------------------"
  56. }

  57. process_list () {
  58.   local ABC="$1"
  59.   local LIST="$2"

  60.   echo
  61.   echo "Choose process mode:"
  62.   echo "1) interactive (逐条处理:delete/keep/link/quit)"
  63.   echo "2) delete ALL"
  64.   echo "3) keep ALL"
  65.   echo "4) create hardlink for ALL (to a directory you specify)"
  66.   echo "5) back"

  67.   read -p "Select: " mode
  68.   [ -z "$mode" ] && return 0
  69.   [ "$mode" = "5" ] && return 0

  70.   local bulk_dir=""
  71.   if [ "$mode" = "4" ]; then
  72.     echo
  73.     echo "Batch link needs a destination directory (will create links as DESTDIR/<basename>)."
  74.     bulk_dir=$(prompt_dir "Enter destination directory for new links" "$ABC/recovered")
  75.   fi

  76.   while IFS= read -r file; do
  77.     [ -z "$file" ] && continue

  78.     if [ ! -e "$file" ]; then
  79.       echo "SKIP (missing now): $file"
  80.       continue
  81.     fi

  82.     action="$mode"
  83.     if [ "$mode" = "1" ]; then
  84.       echo "--------------------------------"
  85.       echo "File: $file"
  86.       echo "1) delete"
  87.       echo "2) keep"
  88.       echo "3) create hardlink (you type full dest path)"
  89.       echo "4) quit"

  90.       read -p "Select: " action
  91.     fi

  92.     if [ "$action" = "1" ]; then
  93.       rm -v -- "$file"

  94.     elif [ "$action" = "2" ] || [ "$action" = "3" ]; then
  95.       if [ "$action" = "2" ]; then
  96.         echo "KEEP: $file"
  97.       else
  98.         read -p "Enter full destination path for new hardlink: " dest
  99.         if [ -z "$dest" ]; then
  100.           echo "No dest, keep."
  101.           continue
  102.         fi
  103.         mkdir -p "$(dirname "$dest")"
  104.         ln -v -- "$file" "$dest"
  105.       fi

  106.     elif [ "$action" = "4" ]; then
  107.       # interactive quit
  108.       return 0

  109.     elif [ "$action" = "4" ] && [ "$mode" != "1" ]; then
  110.       # unreachable
  111.       :

  112.     elif [ "$action" = "4" ] && [ "$mode" = "4" ]; then
  113.       # unreachable
  114.       :

  115.     elif [ "$action" = "4" ]; then
  116.       # unreachable
  117.       :

  118.     elif [ "$action" = "4" ]; then
  119.       # noop
  120.       :

  121.     elif [ "$action" = "4" ]; then
  122.       :

  123.     elif [ "$action" = "4" ]; then
  124.       :

  125.     elif [ "$action" = "4" ]; then
  126.       :

  127.     else
  128.       # batch modes 2/3/4
  129.       if [ "$mode" = "2" ]; then
  130.         rm -v -- "$file"
  131.       elif [ "$mode" = "3" ]; then
  132.         echo "KEEP: $file"
  133.       elif [ "$mode" = "4" ]; then
  134.         bn="$(basename "$file")"
  135.         dest="$bulk_dir/$bn"
  136.         if [ -e "$dest" ]; then
  137.           echo "SKIP (dest exists): $dest"
  138.         else
  139.           mkdir -p "$bulk_dir"
  140.           ln -v -- "$file" "$dest"
  141.         fi
  142.       fi
  143.     fi

  144.   done < "$LIST"

  145.   echo "Process done."
  146. }

  147. main_menu () {
  148.   echo
  149.   echo "========== Hardlink Orphan Tool =========="
  150.   echo "1) Scan and create orphan list"
  151.   echo "2) Process an existing list file"
  152.   echo "3) View a list file"
  153.   echo "4) Quit"
  154.   read -p "Select: " c
  155.   echo "$c"
  156. }

  157. # ---- main ----
  158. echo "This tool identifies files in XYZ with links=1 whose inode is absent in ABC."
  159. echo "Then you can delete/keep/recreate another hardlink."

  160. ABC=$(prompt_dir "Enter ABC (source dir)" "/volume/abc")
  161. XYZ=$(prompt_dir "Enter XYZ (target dir)" "/volume/xyz")

  162. LIST_PATH="$DEFAULT_LIST"

  163. while true; do
  164.   c=$(main_menu)
  165.   case "$c" in
  166.     1)
  167.       read -p "Output list file path [$LIST_PATH]: " out
  168.       LIST_PATH="${out:-$LIST_PATH}"
  169.       scan_orphans "$ABC" "$XYZ" "$LIST_PATH"
  170.       echo
  171.       echo "Next action:"
  172.       echo "1) View list"
  173.       echo "2) Process list now"
  174.       echo "3) Back to main menu"
  175.       read -p "Select: " nxt
  176.       if [ "$nxt" = "1" ]; then
  177.         view_list "$LIST_PATH"
  178.       elif [ "$nxt" = "2" ]; then
  179.         process_list "$ABC" "$LIST_PATH"
  180.       fi
  181.       ;;
  182.     2)
  183.       LIST_PATH=$(prompt_file "Enter list file to process" "$LIST_PATH")
  184.       process_list "$ABC" "$LIST_PATH"
  185.       ;;
  186.     3)
  187.       LIST_PATH=$(prompt_file "Enter list file to view" "$LIST_PATH")
  188.       view_list "$LIST_PATH"
  189.       ;;
  190.     4)
  191.       exit 0
  192.       ;;
  193.     *)
  194.       echo "Unknown option."
  195.       ;;
  196.   esac
  197. done
复制代码


脚本保存在本地,然后执行。
  1. chmod +x hl_orphan_tool.sh
  2. sudo ./hl_orphan_tool.sh
复制代码


进入交互模式:

==== Hardlink Orphan Tool ====

Enter source directory (ABC)          #输入硬链接源目录(其实实质上没有源目录,但是创建硬链接时一般是一些mp或者mdcx等这种视频管理工具,会有一个源整理到目录)
/volume/Video/Downloads   

Enter target directory (XYZ)          #整理的目标目录
/volume/Video/XYZ

Choose action:                       选择任务
1) Scan orphan files            # 扫描目标目录中失去硬链接的文件,并生成orphan_list.txt,后续执行处理操作
2) Process existing list          # 直接使用已生成的orphan_list.txt,后续执行处理操作
3) Exit

这里如果已经扫描过生成了orphan_list.txt,并自己查看无误后,选择2 执行

Enter list file path (default orphan_list.txt)     

Choose process mode:
1) interactive
2) delete all
3) keep all
4) create hardlink for all
2




评论

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2026 绿联NAS私有云社区 版权所有 All Rights Reserved. 粤公网安备44030002002555号| 粤ICP备12028978号
关灯 在本版发帖
联系技术支持
返回顶部
快速回复 返回顶部 返回列表