Знаходження дублікатів файлів та їх заміна символьними посиланнями


16

Я намагаюся знайти спосіб перевірити всередині даного каталогу дублікати файлів (навіть з різними іменами) і замінити їх символьними посиланнями, що вказують на перше виникнення. Я намагався, fdupesале він просто перераховує ці копії.
Ось такий контекст: я налаштовую тему значків на свій смак, і я виявив, що багато іконок, навіть якщо вони мають різні назви та різні місця у своїй батьківській папці, і використовуються для різних цілей, в основному - це саме те саме картина. Оскільки застосування однієї і тієї ж модифікації в двадцять-тридцять разів є зайвим, коли потрібна лише одна, я хочу зберегти лише одне зображення і символізувати всі інші.

Наприклад, якщо я запускаю fdupes -r ./всередині каталогу testdir, він може повернути мені такі результати:

./file1.png
./file2.png
./subdir1/anotherfile.png
./subdir1/subdir2/yetanotherfile.png

З огляду на цей вихід, я хотів би зберегти лише файл file1.png, видалити всі інші та замінити їх на посилання, що вказують на нього, зберігаючи всі оригінальні назви файлів. Так file2.pngзбереже свою назву, але стане посиланням на, file1.pngа не копією.

Ці посилання не повинні вказувати на абсолютний шлях, але повинні бути відносно батьківського testdirкаталогу; тобто yetanotherfile.pngбуде вказувати ../../file1.png, не робити/home/testuser/.icons/testdir/file1.png

Мене цікавлять і рішення, які передбачають графічний інтерфейс та CLI. Це не обов'язкове використання fdupesЯ цитував це, тому що це інструмент, який я знаю, але я відкритий для рішень, які використовують і інші інструменти.

Я майже впевнений, що bash-скрипт для обробки всього цього не повинен бути таким складним для створення, але я недостатньо досвідчений, щоб з’ясувати, як його написати самостійно.

Відповіді:


3

Спочатку; Чи є причина, що вам потрібно використовувати символьні посилання, а не звичайні жорсткі посилання? Мені важко зрозуміти потребу в посиланнях із відносними шляхами. Ось як я вирішив цю проблему:

Я думаю, що версія fdupes Debian (Ubuntu) може замінити дублікати жорсткими посиланнями, використовуючи -Lопцію, але у мене немає встановлення Debian, щоб це підтвердити.

Якщо у вас немає версії з -Lопцією, ви можете використовувати цей крихітний скрипт bash, який я знайшов у командному рядку .
Зауважте, що цей синтаксис працюватиме лише в базі.

fdupes -r -1 path | while read line; do master=""; for file in ${line[*]}; do if [ "x${master}" == "x" ]; then master=$file; else ln -f "${master}" "${file}"; fi; done; done

Вищевказана команда знайде всі дублікати файлів на "шляху" та замінить їх жорсткими посиланнями. Ви можете перевірити це, запустивши ls -ilRі переглянувши номер вводу. Ось зразок з десятьма однаковими файлами:

$ ls -ilR

total 20
3094308 -rw------- 1 username group  5 Sep 14 17:21 file
3094311 -rw------- 1 username group  5 Sep 14 17:21 file2
3094312 -rw------- 1 username group  5 Sep 14 17:21 file3
3094313 -rw------- 1 username group  5 Sep 14 17:21 file4
3094314 -rw------- 1 username group  5 Sep 14 17:21 file5
3094315 drwx------ 1 username group 48 Sep 14 17:22 subdirectory

./subdirectory:
total 20
3094316 -rw------- 1 username group 5 Sep 14 17:22 file
3094332 -rw------- 1 username group 5 Sep 14 17:22 file2
3094345 -rw------- 1 username group 5 Sep 14 17:22 file3
3094346 -rw------- 1 username group 5 Sep 14 17:22 file4
3094347 -rw------- 1 username group 5 Sep 14 17:22 file5

Усі файли мають окремі номери номерів, що робить їх окремими файлами. Тепер давайте повторювати їх:

$ fdupes -r -1 . | while read line; do j="0"; for file in ${line[*]}; do if [ "$j" == "0" ]; then j="1"; else ln -f ${line// .*/} $file; fi; done; done
$ ls -ilR
.:
total 20
3094308 -rw------- 10 username group  5 Sep 14 17:21 file
3094308 -rw------- 10 username group  5 Sep 14 17:21 file2
3094308 -rw------- 10 username group  5 Sep 14 17:21 file3
3094308 -rw------- 10 username group  5 Sep 14 17:21 file4
3094308 -rw------- 10 username group  5 Sep 14 17:21 file5
3094315 drwx------  1 username group 48 Sep 14 17:24 subdirectory

./subdirectory:
total 20
3094308 -rw------- 10 username group 5 Sep 14 17:21 file
3094308 -rw------- 10 username group 5 Sep 14 17:21 file2
3094308 -rw------- 10 username group 5 Sep 14 17:21 file3
3094308 -rw------- 10 username group 5 Sep 14 17:21 file4
3094308 -rw------- 10 username group 5 Sep 14 17:21 file5

Тепер усі файли мають однаковий номер введення, тобто всі вони вказують на однакові фізичні дані на диску.

Я сподіваюся, що це вирішить вашу проблему або принаймні вказує на вас у правильному напрямку!


Я згадав fdupes, що має можливість замінити дупи на посилання, @arnefm, але я нічого не бачу в людині, і це не варіант v1.51(Ubuntu 14.04.2 LTS).
Аластер

Моя вилка за jdupesадресою github.com/jbruchon/jdupes має -Lможливість встановити бажане жорстке з'єднання дублікатів.
Джоді Лі Брюшон

Я щойно тут переробив сценарій. Він все ще не буде обробляти пробіли, але буде обробляти інші спеціальні символи (у мене були рядки запитів URL у файлах). Крім того, ${line//…/}частина не працювала для мене, тому я зробив більш чистий спосіб отримати перший "головний" файл на жорстке посилання.
IBBoard

1
Чи потрібні нам відносні програмні посилання, якщо ми використовуємо rsyncфайлову систему іншого типу? Або якщо файлова система не зберігає ієрархію, наприклад, це резервний сервер, який підводить все /«machine-name»/...? Або якщо ви хочете відновити з резервної копії? Я не бачу, як жорсткі посилання збережуться тут. Я думаю, що відносні програмні посилання мали б більший шанс вижити.
Бадді

6

Якщо ви не дуже любите сценарії, то я можу порекомендувати rdfind . Які скануватимуть задані каталоги на наявність дублікатів файлів і з'єднують їх жорстко або з програмним забезпеченням. Я використовував його для того, щоб з великим успіхом присвоїти мені каталог Ruby gems. Він доступний у Debian / Ubuntu.


4

У мене була подібна ситуація, але в моєму випадку символічне посилання повинно вказувати на відносний шлях, тому я написав цей сценарій пітона, щоб зробити трюк:

#!/usr/bin/env python
# Reads fdupes(-r -1) output and create relative symbolic links for each duplicate
# usage: fdupes -r1 . | ./lndupes.py

import os
from os.path import dirname, relpath, basename, join
import sys

lines = sys.stdin.readlines()

for line in lines:
    files = line.strip().split(' ')
    first = files[0]
    print "First: %s "% first
    for dup in files[1:]:
        rel = os.path.relpath(dirname(first), dirname(dup))
        print "Linking duplicate: %s to %s" % (dup, join(rel,basename(first)))
        os.unlink(dup)
        os.symlink(join(rel,basename(first)), dup)

Для кожного рядка введення (який є списком файлів) сценарій розбиває список файлів (пробіл розділений), отримує відносний шлях від кожного файлу до першого, а потім створює симпосилання.


1

Отже, відповідь, яку дав arnefm (це було скопійовано в Інтернеті), не стосується пробілів у назвах файлів. Я написав сценарій, який стосується пробілів у файлах.

#!/bin/bash
fdupes -r -1 CHANGE_THIS_PATH | sed -e 's/\(\w\) /\1|/g' -e 's/|$//' > files
while read line; do
        IFS='|' read -a arr <<< "$line"
        orig=${arr[0]}
        for ((i = 1; i < ${#arr[@]}; i++)); do
                file="${arr[$i]}"
                ln -sf "$orig" "$file"
        done 
done < files

Для цього потрібно знайти дупи та записати їх PIPE, розділених на файл з назвою "файли".

Потім він читає файл назад, рядок за рядком, у масив, і кожен елемент масиву розмежовується PIPE.

Потім він повторює всі не перші елементи масиву, замінюючи файл символьним посиланням на перший елемент.

Зовнішній файл ("файли") можна було б видалити, якщо команда fdupes буде виконана в нижній частині, який час читається безпосередньо, але цей спосіб здається зрозумілішим.


2
Чи стосується ця версія файлів з іменами, що містять трубу? Я припускаю, що жодна версія не обробляє імена файлів, що містять нові рядки, але це обмеження fdupes, а не все інше.
даг

Це не так, але ви можете встановити IFS на все, що завгодно (також змінити значення в заміні sed), тоді у вас не повинно виникнути жодних проблем (IFS на '-' або щось подібне повинно працювати)
David Ventura

Це створює зламані символьні посилання, і у мене є файли, пов'язані з собою. НЕ ВИКОРИСТОВУЙТЕ
MrMesees

0

Деякі застереження спереду:

  • BASH специфічні
  • Немає місця в іменах файлів
  • Припустимо, що кожен рядок містить максимум 2 файли.

fdupes -1r common/base/dir | while read -r -a line ; do ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]}; done

Якщо більше двох файлів є дублікатами (наприклад, файл1 файл2 файл3), то нам потрібно створити симпосилання для кожної пари - розгляньте файл1, файл2 та файл1, файл3 як два окремих випадки:

if [[ ${#line[@]} -gt 2 ]] ;then 
  ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]} 
  ln -sf $(realpath --relative-to ${line[2]} ${line[0]}) ${line[2]} 
  ...
fi

Якщо розширити цю функцію для автоматичної обробки довільної кількості дублікатів на рядок, знадобиться трохи більше зусиль.

Іншим підходом було б спочатку створити посилання на абсолютні контури, а потім перетворити їх:

fdupes -1r /absolute/path/common/base/dir | while read -r -a line ; do ln -sf ${line[0]} ${line[1]}; done
chroot /absolute/path/common/base/dir ; symlinks -cr .

Це ґрунтується на відповіді @Gilles: /unix//a/100955/77319

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.