Tìm tập tin trùng lặp và thay thế chúng bằng symlink


16

Tôi đang cố gắng tìm cách kiểm tra bên trong một thư mục nhất định cho các tệp trùng lặp (ngay cả với các tên khác nhau) và thay thế chúng bằng các liên kết tượng trưng chỉ đến lần xuất hiện đầu tiên. Tôi đã thử với fdupesnhưng nó chỉ liệt kê những bản sao đó.
Đó là bối cảnh: Tôi đang tùy chỉnh một chủ đề biểu tượng theo ý thích của mình và tôi đã tìm thấy nhiều biểu tượng, ngay cả khi chúng có tên khác nhau và vị trí khác nhau trong thư mục mẹ của chúng và được sử dụng cho các mục đích khác nhau, về cơ bản là giống nhau hình ảnh. Vì việc áp dụng cùng một sửa đổi hai mươi hoặc ba mươi lần là không cần thiết khi chỉ cần một lần thực sự cần thiết, tôi muốn chỉ giữ một hình ảnh và liên kết tượng trưng cho tất cả các hình ảnh khác.

Ví dụ, nếu tôi chạy fdupes -r ./trong thư mục testdir, nó có thể trả về cho tôi các kết quả sau:

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

Với đầu ra này, tôi chỉ muốn giữ lại tệp file1.png, xóa tất cả các tệp khác và thay thế chúng bằng các liên kết tượng trưng trỏ đến nó, trong khi duy trì tất cả các tên tệp gốc. Vì vậy, file2.pngsẽ giữ lại tên của nó, nhưng sẽ trở thành một liên kết đến file1.pngthay vì là một bản sao.

Các liên kết đó không nên trỏ đến một đường dẫn tuyệt đối, mà nên liên quan đến testdirthư mục cha ; tức là yetanotherfile.pngsẽ được trỏ đến ../../file1.png, không phải/home/testuser/.icons/testdir/file1.png

Tôi quan tâm đến cả các giải pháp liên quan đến GUI và CLI. Không bắt buộc phải sử dụng fdupesTôi đã trích dẫn vì đây là công cụ mà tôi biết, nhưng tôi cũng mở cho các giải pháp sử dụng các công cụ khác.

Tôi khá chắc chắn rằng một tập lệnh bash để xử lý tất cả những điều này không quá khó để tạo ra, nhưng tôi không đủ chuyên môn để tự tìm ra cách viết nó.

Câu trả lời:


3

Đầu tiên; Có một lý do bạn cần sử dụng symlink chứ không phải các liên kết cứng thông thường? Tôi đang có một thời gian khó khăn để hiểu sự cần thiết của các liên kết tượng trưng với các đường dẫn tương đối. Đây là cách tôi sẽ giải quyết vấn đề này:

Tôi nghĩ rằng phiên bản fdupes Debian (Ubuntu) có thể thay thế các bản sao bằng các liên kết cứng bằng -Ltùy chọn, nhưng tôi không có cài đặt Debian để xác minh điều này.

Nếu bạn không có phiên bản với -Ltùy chọn, bạn có thể sử dụng tập lệnh bash nhỏ này mà tôi tìm thấy trên dòng lệnh .
Lưu ý rằng cú pháp này sẽ chỉ hoạt động trong 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

Lệnh trên sẽ tìm thấy tất cả các tệp trùng lặp trong "đường dẫn" và thay thế chúng bằng các liên kết cứng. Bạn có thể xác minh điều này bằng cách chạy ls -ilRvà nhìn vào số inode. Đây là một samle với mười tập tin giống hệt nhau:

$ 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

Tất cả các tệp có số inode riêng biệt, làm cho chúng các tệp riêng biệt. Bây giờ cho phép sao chép chúng:

$ 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

Các tệp bây giờ đều có cùng số inode, nghĩa là tất cả chúng đều trỏ đến cùng một dữ liệu vật lý trên đĩa.

Tôi hy vọng điều này sẽ giải quyết vấn đề của bạn hoặc ít nhất là chỉ cho bạn đi đúng hướng!


Tôi nhớ lại các fdup có một tùy chọn để thay thế các bản sao bằng các liên kết, @arnefm nhưng tôi không thể thấy bất cứ điều gì ở người đàn ông cũng không phải là một tùy chọn trong v1.51(Ubuntu 14.04.2 LTS).
Alastair

Cái nĩa của tôi jdupestại github.com/jbruchon/jdupes-Ltùy chọn thực hiện liên kết cứng mong muốn của các bộ trùng lặp.
Jody Lee Bruchon

Tôi vừa mới điều chỉnh kịch bản ở đây. Nó vẫn không xử lý khoảng trắng, nhưng sẽ xử lý các ký tự đặc biệt khác (tôi có chuỗi truy vấn URL trong tệp). Ngoài ra, ${line//…/}phần này không hoạt động với tôi, vì vậy tôi đã làm một cách sạch hơn để đưa tệp "chính" đầu tiên vào liên kết cứng.
IBBoard

1
Chúng ta có cần các liên kết mềm tương đối nếu chúng ta sử dụng rsyncmột loại hệ thống tệp khác không? Hoặc nếu hệ thống tệp không bảo toàn cấu trúc phân cấp, ví dụ: đó là máy chủ dự phòng đặt mọi thứ bên dưới /«machine-name»/...? Hoặc nếu bạn muốn khôi phục từ bản sao lưu? Tôi không thể thấy các liên kết cứng sẽ được bảo tồn ở đây như thế nào. Liên kết mềm tương đối sẽ có cơ hội sống sót tốt hơn, tôi có thể nghĩ.
Buddy

6

Nếu bạn không thích kịch bản nhiều thì tôi có thể giới thiệu rdfind . Sẽ quét các thư mục đã cho cho các tệp trùng lặp và liên kết cứng hoặc mềm với nhau. Tôi đã sử dụng nó để sao chép thư mục đá quý Ruby của tôi rất thành công. Nó có sẵn trong Debian / Ubuntu.


4

Tôi đã có một tình huống tương tự, nhưng trong trường hợp của tôi, liên kết tượng trưng nên trỏ đến một đường dẫn tương đối vì vậy tôi đã viết kịch bản python này để thực hiện thủ thuật:

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

Đối với mỗi dòng đầu vào (là danh sách các tệp), tập lệnh sẽ tách danh sách tệp (khoảng trắng được phân tách), lấy đường dẫn tương đối từ mỗi tệp đến tệp đầu tiên và sau đó tạo liên kết tượng trưng.


1

Vì vậy, câu trả lời được đưa ra bởi arnefm (đã được sao chép trên internet) không xử lý các khoảng trắng trong tên tệp. Tôi đã viết một tập lệnh liên quan đến khoảng trắng trong tệp.

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

Những gì nó làm là tìm bản sao và viết chúng PIPE tách thành một tệp có tên là 'tệp'.

Sau đó, nó đọc lại tệp, từng dòng, thành một mảng và mỗi phần tử của mảng được phân định bởi PIPE.

Sau đó, nó lặp đi lặp lại trên tất cả các phần tử không phải đầu tiên của mảng, thay thế tệp bằng một liên kết tượng trưng đến phần tử đầu tiên.

Tệp bên ngoài ('tệp') có thể bị xóa, nếu lệnh fdupes được thực thi trong một khung con, điều đó được đọc trực tiếp trong khi đó, nhưng cách này có vẻ rõ ràng hơn.


2
Phiên bản này có xử lý các tệp có tên chứa đường ống không? Tôi giả sử không có phiên bản nào xử lý tên tệp có chứa dòng mới, nhưng đó là hạn chế của fdupes hơn là bất kỳ thứ gì khác.
dhag 14/07/2015

Không, nhưng bạn có thể đặt IFS thành bất cứ điều gì bạn muốn (cũng sửa đổi giá trị trong thay thế sed), sau đó bạn không nên có bất kỳ vấn đề gì (IFS thành 'ñ' hoặc một cái gì đó tương tự như vậy)
David Ventura

Điều này tạo ra các liên kết tượng trưng bị hỏng và tôi có các tệp được liên kết với chính chúng. KHÔNG SỬ DỤNG
MrMesees

0

Một số hãy cẩn thận lên phía trước:

  • BASH cụ thể
  • Không có khoảng trống trong tên tệp
  • Giả sử mỗi dòng chứa tối đa 2 tệp.

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

Nếu có nhiều hơn 2 tệp trùng lặp (ví dụ: file1 file2 file3) thì chúng ta cần tạo một liên kết tượng trưng cho mỗi cặp - coi file1, file2 và file1, file3 là 2 trường hợp riêng biệt:

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

Việc này sẽ tự động xử lý một số lượng trùng lặp tùy ý trên mỗi dòng sẽ tốn nhiều công sức hơn một chút.

Một cách tiếp cận khác là trước tiên hãy tạo liên kết tượng trưng đến các đường dẫn tuyệt đối, sau đó chuyển đổi chúng:

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 .

Điều này dựa trên câu trả lời của @Gilles: /unix//a/100955/77319

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.