Menemukan file duplikat dan menggantinya dengan symlink


16

Saya mencoba menemukan cara untuk memeriksa di dalam direktori tertentu untuk file duplikat (bahkan dengan nama yang berbeda) dan menggantinya dengan symlink yang menunjuk pada kejadian pertama. Saya sudah mencoba fdupestetapi hanya mencantumkan duplikat itu.
Itulah konteksnya: Saya menyesuaikan tema ikon sesuai dengan keinginan saya, dan saya telah menemukan banyak ikon, bahkan jika mereka memiliki nama dan lokasi berbeda di dalam folder induknya, dan digunakan untuk tujuan yang berbeda, pada dasarnya sama saja gambar. Karena menerapkan modifikasi yang sama dua puluh atau tiga puluh kali berlebihan ketika hanya satu yang benar-benar diperlukan, saya ingin menyimpan satu gambar dan menghubungkan semua gambar lainnya.

Sebagai contoh, jika saya menjalankan fdupes -r ./di dalam direktori testdir, itu mungkin kembali kepada saya hasil berikut:

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

Dengan hasil ini, saya hanya ingin menyimpan file file1.png, menghapus semua yang lain dan menggantinya dengan symlink yang menunjuk ke sana, sambil mempertahankan semua nama file asli. Jadi file2.pngakan mempertahankan namanya, tetapi akan menjadi tautan untuk file1.pngbukannya menjadi duplikat.

Tautan tersebut tidak boleh mengarah ke jalur absolut, tetapi harus relatif terhadap testdirdirektori induk ; yaitu yetanotherfile.pngakan menunjuk ke ../../file1.png, bukan ke/home/testuser/.icons/testdir/file1.png

Saya tertarik pada solusi yang melibatkan GUI dan CLI. Itu tidak wajib untuk menggunakan fdupessaya telah mengutipnya karena ini adalah alat yang saya tahu, tapi saya terbuka untuk solusi yang menggunakan alat lain juga.

Saya cukup yakin bahwa skrip bash untuk menangani semua ini seharusnya tidak terlalu sulit untuk dibuat, tetapi saya tidak cukup ahli untuk mengetahui cara menulisnya sendiri.

Jawaban:


3

Pertama; Apakah ada alasan Anda perlu menggunakan symlink dan bukan hardlink yang biasa? Saya mengalami kesulitan memahami perlunya symlink dengan jalur relatif. Inilah cara saya memecahkan masalah ini:

Saya pikir versi fdupes Debian (Ubuntu) dapat menggantikan duplikat dengan tautan keras menggunakan -Lopsi, tetapi saya tidak memiliki instalasi Debian untuk memverifikasi ini.

Jika Anda tidak memiliki versi dengan -Lopsi, Anda dapat menggunakan skrip bash kecil ini yang saya temukan di commandlinefu .
Perhatikan bahwa sintaks ini hanya akan bekerja di 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

Perintah di atas akan menemukan semua file duplikat di "path" dan menggantinya dengan hardlink. Anda dapat memverifikasi ini dengan menjalankan ls -ilRdan melihat nomor inode. Berikut ini adalah samle dengan sepuluh file identik:

$ 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

Semua file memiliki nomor inode yang terpisah, menjadikannya file yang terpisah. Sekarang mari kita deduplikasi mereka:

$ 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

File-file sekarang semua memiliki nomor inode yang sama, artinya semuanya menunjuk ke data fisik yang sama pada disk.

Saya harap ini menyelesaikan masalah Anda atau setidaknya mengarahkan Anda ke arah yang benar!


Saya ingat fdupes memiliki opsi untuk mengganti dupes dengan tautan, @arnefm, tetapi saya tidak dapat melihat apa pun di dalam man tersebut juga bukan opsi di v1.51(Ubuntu 14.04.2 LTS).
Alastair

Garpu saya jdupesdi github.com/jbruchon/jdupes memiliki -Lopsi untuk melakukan tautan keras set duplikat yang diinginkan.
Jody Lee Bruchon

Saya baru saja mengubah skrip di sini. Itu masih tidak akan menangani spasi, tetapi akan menangani karakter khusus lainnya (saya punya string kueri URL dalam file). Juga, ${line//…/}bagian itu tidak berfungsi untuk saya, jadi saya melakukan cara yang lebih bersih untuk mendapatkan file "master" pertama ke hardlink.
IBBoard

1
Apakah kita memerlukan softlink relatif jika kita menggunakan rsyncsistem file yang berbeda? Atau jika sistem file tidak mempertahankan hierarki, mis. Itu adalah server cadangan yang meletakkan semuanya di bawah /«machine-name»/...? Atau jika Anda ingin memulihkan dari cadangan? Saya tidak bisa melihat bagaimana hardlink akan dipertahankan di sini. Softlink relatif akan memiliki peluang lebih baik untuk bertahan hidup, saya pikir.
Buddy

6

Jika Anda tidak suka banyak scripting maka saya dapat merekomendasikan rdfind . Yang akan memindai direktori yang diberikan untuk file duplikat dan menghubungkannya dengan hard atau soft. Saya telah menggunakannya untuk deduplicating direktori Ruby gems saya dengan sangat sukses. Ini tersedia di Debian / Ubuntu.


4

Saya memiliki situasi yang serupa, tetapi dalam kasus saya tautan simbolik harus mengarah ke jalur relatif sehingga saya menulis skrip python ini untuk melakukan trik:

#!/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)

Untuk setiap baris input (yang merupakan daftar file) skrip membagi daftar file (dipisahkan spasi), mendapatkan jalur relatif dari setiap file ke yang pertama dan kemudian membuat symlink.


1

Jadi, jawaban yang diberikan oleh arnefm (yang telah disalin di internet) tidak berurusan dengan spasi dalam nama file. Saya telah menulis skrip yang berkaitan dengan spasi dalam file.

#!/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

Yang dilakukan adalah menemukan dupes dan menuliskannya PIPE yang dipisahkan menjadi file bernama 'file'.

Kemudian ia membaca file kembali, baris demi baris, menjadi sebuah array, dan setiap elemen dari array dibatasi oleh PIPE.

Ini kemudian mengulangi semua elemen non-pertama dari array, mengganti file dengan symlink ke elemen pertama.

File eksternal ('file') dapat dihapus, jika perintah fdupes dijalankan dalam subkulit, yang dibaca langsung oleh sementara, tetapi cara ini tampaknya lebih jelas.


2
Apakah versi ini berurusan dengan file dengan nama yang mengandung pipa? Saya berasumsi versi tidak menangani nama file yang mengandung baris baru, tetapi itu adalah batasan dari fdupes daripada yang lainnya.
Dhag

Tidak, tetapi Anda dapat mengatur IFS untuk apa pun yang Anda inginkan (juga memodifikasi nilai dalam penggantian sed), maka Anda seharusnya tidak memiliki masalah (IFS menjadi 'ñ' atau sesuatu seperti itu seharusnya bekerja)
David Ventura

Ini menciptakan symlink yang rusak, dan saya memiliki file yang tertaut ke dirinya. JANGAN GUNAKAN
MrMesees

0

Beberapa peringatan di depan:

  • Khusus BASH
  • Tidak ada ruang dalam nama file
  • Asumsikan setiap baris paling banyak berisi 2 file.

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

Jika lebih dari 2 file adalah duplikat (mis. File1 file2 file3) maka kita perlu membuat symlink untuk setiap pasangan - perlakukan file1, file2 dan file1, file3 sebagai 2 kasus terpisah:

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

Mengeluarkan ini untuk secara otomatis menangani jumlah duplikat per baris yang sewenang-wenang akan membutuhkan sedikit usaha lebih banyak.

Pendekatan lain adalah dengan pertama-tama membuat symlink ke path absolut, kemudian mengubahnya:

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 .

Ini didasarkan pada jawaban oleh @Gilles: /unix//a/100955/77319

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.