Apakah ada cara menghapus duplikat yang lebih disempurnakan daripada fdupes -rdN?

22

Baru-baru ini saya perlu menghapus banyak duplikat. Saya menggabungkan tiga atau empat sistem file, dan saya ingin ruang tersebut digunakan secara ekonomis. Pada awalnya, fdupessepertinya itu adalah alat terbaik untuk pekerjaan itu, tetapi saya semakin mengalami keterbatasan.

Pertimbangkan perintahnya fdupes -rdN somedirectory/. Ini membuat hash dari semua file di subdirektori dari suatu direktori.

Dan ketika menemukan duplikat, itu menghapus mereka, sehingga hanya ada satu salinan dari segalanya.

Tetapi bagaimana jika saya ingin menyimpan somedirectory/subdirectory1/somefiledan ternyata ada empat duplikat, dan program pertama kali bertemu dengan salah satu duplikat? Kemudian menghapus somedirectory/subdirectory1/somefile, yang saya tidak inginkan.

Saya ingin dapat menentukan, entah bagaimana, duplikat mana yang harus disimpan. Dan sejauh ini, tidak ada program standar untuk menangani duplikat (duff, FSLint) tampaknya memungkinkan otomatisasi perilaku semacam itu. Saya lebih suka tidak menggulung sendiri, jadi itu sebabnya saya menanyakan pertanyaan ini.

Saya ingin dapat menulis sesuatu seperti

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
ixtmixilix
sumber
Saya sedang mencari hal yang sama dan saya menemukan superuser.com/a/561207/218922
alexis

Jawaban:

5

Meskipun fungsi yang Anda cari tidak tersedia dalam stok fdupes, saya bercabang fdupes (garpu saya dipanggil jdupes) dan menambahkan beberapa fitur yang dapat menyelesaikan masalah ini dalam keadaan tertentu. Misalnya, dalam kasus yang dinyatakan di mana Anda ingin menyimpan somedirectory/subdirectory1/somefileketika menghapus duplikat otomatis ( ddan Nberalih bersama) dan tidak ada file terpisah segera di bawahnya somedirectory, jdupesdapat dimasukkan setiap jalur subdirektori langsung dengansubdirectory1 pertama dan -Oswitch (yang mengurutkan file dengan perintah -pesanan parameter baris pertama):

jdupes -nrdNO somedirectory/subdirectory1 somedirectory/subdirectory2 somedirectory/subdirectory3

Ini akan secara otomatis menghapus semua kecuali satu file dalam set duplikat dan akan menjamin bahwa jika set berisi file di somedirectory/subdirectory1dalamnya akan menjadi yang pertama, sehingga secara otomatis menjadi file yang diawetkan dalam set. Masih ada batasan mencolok untuk pendekatan ini seperti fakta bahwa duplikat lain somedirectory/subdirectory1mungkin dipertahankan daripada yang ingin Anda pertahankan, tetapi dalam sejumlah kasus seperti Anda, jdupesopsi urutan parameter sebagai solusi sudah cukup baik.

Dalam waktu dekat, saya berencana untuk menambahkan sistem pemfilteran jdupesyang akan memungkinkan sejumlah besar kontrol atas penyertaan / pengecualian file, pelestarian untuk -Ntindakan, dan penerapan "tumpukan filter" tersebut baik secara global atau per-parameter. Fitur ini sangat dibutuhkan; Saya membayangkan sesuatu seperti ini untuk "menghapus secara otomatis duplikat yang bukan nol tetapi TETAP selalu mempertahankan somedirectory/subdirectory1/somefileapa adanya":

jdupes -nrdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/

Jody Lee Bruchon
sumber
4

Bagaimana dengan menghubungkan file duplikat bersama-sama? Dengan cara itu ruang hanya digunakan sekali, tetapi mereka masih ada di semua jalur. Yang menarik dari hal ini adalah bahwa file yang di-hardlink harus dimodifikasi di tempatnya (mereka hanya boleh dimodifikasi menghapus file dan membuatnya kembali dengan konten baru). Pendekatan lainnya adalah dengan symlink file bersama, meskipun Anda memiliki masalah yang sama memutuskan mana file "primer". Ini dapat dilakukan dengan skrip berikut (walaupun perhatikan bahwa ini tidak menangani nama file yang mengandung spasi).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
    for DEST in $DESTS; do
        ln -f $SOURCE $DEST
    done
done
Mgorven
sumber
1
Menggunakan jdupesalih-alih fdupesAnda hanya bisa pergi jdupes -nrL somedirectory/yang secara besar-besaran lebih cepat.
Jody Lee Bruchon
1
Mengetik di tautan ke jdupes. Tautan kenyamanan: github.com/jbruchon/jdupes
Royce Williams
4

Saya tidak melihat yang ini di tempat lain: Katakan apa yang Anda inginkan adalah ini. Anda memiliki / mnt / folder-tree-1 / mnt / folder-tree-2. Anda tidak ingin menghapus setiap dupe, tetapi jika file ada di tree-2, dan file identik ada di tree-1 dengan path dan nama yang persis sama, hapus dari tree-2.

Peringatan: ini cukup singkat dan jika Anda mencoba menyalin-menempelkan ini dengan keterampilan shell yang terbatas, berhati-hatilah.

fdupes -rn /mnt/folder-tree-1/ /mnt/folder-tree-2/ > dupes-all.txt

fgrep /mnt/folder-tree-1/ dupes-all.txt | while read line
do
if grep -q "`echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|'`" dupes-all.txt
then
    echo rm \"$(echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2//|')\"
fi
done > rm-v2-dupes.sh

Atau semua dalam satu baris:

fdupes -rn /mnt/folder-tree-1/ /mnt/folder-tree-2/ > dupes-all.txt; fgrep /mnt/folder-tree-1/ dupes-all.txt | while read line; do if grep -q "`echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|'`" dupes-all.txt; then echo rm \"$(echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|')\"; fi; done > rm-v2-dupes.sh

Setelah itu, periksa dan jalankan rm-v2-dupes.sh

Gaute Lund
sumber
4

Saya punya pertanyaan yang sama. Jika Anda memiliki banyak duplikatfdupes /my/directory/ -rdN menyimpan file dengan tanggal modifikasi terlama, atau jika beberapa file memiliki tanggal modifikasi yang sama, maka yang ditemukan pertama kali.

Jika tanggal modifikasi tidak penting bagi Anda, Anda dapat touchmenyimpan file di direktori yang ingin Anda simpan. Jika Anda memilih touchmereka dengan tanggal dan waktu saat ini maka mereka fdupes -rdNiakan tetap dengan tanggal saat ini. Atau Anda dapat touchmenyimpan file dengan tanggal lebih awal dari tanggal yang ingin Anda hapus dan gunakan fdupes -rdNseperti biasa.

Jika Anda perlu menyimpan tanggal modifikasi, maka Anda harus menggunakan salah satu metode lain.

pheon
sumber
3

Hanya dengan menambahkan twist pada jawaban sebelumnya. Saya telah menggunakan kode berikut beberapa kali, sedikit memodifikasi jawaban sebelumnya dengan sederhana | grepuntuk mengisolasi folder yang ingin saya hapus.

`fdupes -r -n -S /directory | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh`

Sekali lagi, ini akan membuat file sh untuk menghapus semua file yang terdaftar, tidak ada baris yang dikomentari. Tentu saja Anda masih dapat mengedit file untuk mengomentari baris / file tertentu yang ingin Anda pertahankan.

Petunjuk lain untuk direktori besar adalah menjalankan fdupes ke file txt, kemudian bereksperimen dengan | grepdan | sedsampai saya mendapatkan hasil yang saya inginkan.

`fdupes -r -n -S /directory > duplicate-files.txt`
`cat duplicate-files.txt | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh`
jfl
sumber
2

Gunakan seduntuk membuat file shell yang akan berisi perintah komentar untuk menghapus setiap file duplikat Anda:

fdupes -r -n -S /directory | sed -r "s/^/#rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh

File yang dihasilkan remove-duplicate-files.shyang baru saja kita buat akan membuat setiap baris berkomentar. Batalkan komentar pada file yang ingin Anda hapus. Kemudian jalankan sh remove-duplicate-files.sh. Voila!

MEMPERBARUI

Nah, jika Anda tidak ingin menghapus file hanya di direktori tertentu, sesederhana ini :

fdupes -S /directory|sed '/^$/d' |sed -r "s/^[0-9]/#&/" > duple_list

python exclude_duplicates.py -f /path/to/dupe_list --delimiter='#' --keep=/full/path/to/protected/directory1,/full/path/to/protected/directory2\ with\ spaces\ in\ path >remove-duplicate-files-keep-protected.sh

Dimana exclude_duplicates.py:

#/usr/bin/python
# -*- coding: utf-8 -*-
# exclude_duplicates.py
"""
THE SCRIPT DOESN'T DELETE ANYTHING, IT ONLY GENERATES TEXT OUTPUT.
Provided a list of duplicates, such as fdupes or fslint output,
generate a bash script that will have all duplicates in protected
directories commented out. If none of the protected duplicates are
found in a set of the same files, select a random unprotected
duplicate for preserving.
Each path to a file will be transformed to an `rm "path"` string which
will be printed to standard output.     
"""

from optparse import OptionParser
parser = OptionParser()
parser.add_option("-k", "--keep", dest="keep",
    help="""List of directories which you want to keep, separated by commas. \
        EXAMPLE: exclude_duplicates.py --keep /path/to/directory1,/path/to/directory\ with\ space\ in\ path2""",
    metavar="keep"
)
parser.add_option("-d", "--delimiter", dest="delimiter",
    help="Delimiter of duplicate file groups", metavar="delimiter"
)
parser.add_option("-f", "--file", dest="file",
    help="List of duplicate file groups, separated by delimiter, for example, fdupes or fslint output.", metavar="file"
)

(options, args) = parser.parse_args()
directories_to_keep = options.keep.split(',')
file = options.file
delimiter = options.delimiter

pretty_line = '\n#' + '-' * 35
print '#/bin/bash'
print '#I will protect files in these directories:\n'
for d in directories_to_keep:
    print '# ' + d
print pretty_line

protected_set = set()
group_set = set()

def clean_set(group_set, protected_set, delimiter_line):
    not_protected_set = group_set - protected_set
    while not_protected_set:
        if len(not_protected_set) == 1 and len(protected_set) == 0:
            print '#randomly selected duplicate to keep:\n#rm "%s"' % not_protected_set.pop().strip('\n')
        else:
            print 'rm "%s"' % not_protected_set.pop().strip('\n')
    for i in protected_set: print '#excluded file in protected directory:\n#rm "%s"' % i.strip('\n')
    print '\n#%s' % delimiter_line
file = open(file, 'r')
for line in file.readlines():
    if line.startswith(delimiter):
        clean_set(group_set, protected_set, line)
        group_set, protected_set = set(), set()
    else:
        group_set = group_set|{line}
        for d in directories_to_keep:
            if line.startswith(d): protected_set = protected_set|{line}
else:
    if line: clean_set(group_set, protected_set, line)

File yang dihasilkan remove-duplicate-files-keep-protected.shyang baru saja kita buat akan mengomentari semua file dari direktori yang dilindungi. Buka file ini di editor teks favorit Anda, periksa apakah semuanya OK. Kemudian jalankan. Voila!

Ivan Kharlamov
sumber
Saya memikirkan hal ini, tetapi tidak cukup otomatis. bodohnya, saya menyebabkan kehilangan data dengan metode ini ketika berhadapan dengan duplikat yang ditempatkan di beberapa sistem file ... tidak ada cara untuk menetapkan prioritas, mengingat output dari fdupes. pada dasarnya saya harus menjaring 10.000 file dengan tangan untuk mencegah kehilangan data ... jadi, tidak, terima kasih ... sebenarnya, kehilangan data adalah alasan utama saya menanyakan pertanyaan ini.
ixtmixilix
@ixtmixilix, well, metode manual tergantung pada perhatian pengguna, ini bukan hal yang baru. Jika Anda menginginkan sesuatu yang lebih otomatis, periksa jawaban yang diperbarui di atas.
Ivan Kharlamov
2

Bagaimana dengan sesuatu yang seperti ini?

#!/bin/bash

DUPE_SEARCH_DIR=somedir/
PREFERRED_DIRS=("somedir/subdir1" "somedir/subdir2")
DUPE_FILE=/tmp/`basename $0`_found-duplicates

delete_dupes() {
    while read line ; do
        if [ -n "$line" ] ; then
            matched=false
            for pdir in "${PREFERRED_DIRS[@]}" ; do
                if [[ $line == $pdir/* ]] ; then
                    matched=true
                    break
                fi
            done
            if ! $matched ; then
                rm -v "$line"
            fi
        fi
    done < "$DUPE_FILE"
}

cleanup() {
    rm -f $DUPE_FILE
}

trap cleanup EXIT

# get rid of normal dupes, preserve first & preserve preferred
fdupes -rf "$DUPE_SEARCH_DIR" > $DUPE_FILE
delete_dupes

# get rid of preserve dupes, preserve preferred
fdupes -r "$DUPE_SEARCH_DIR" > "$DUPE_FILE"
delete_dupes
Rynchodon
sumber