Yinelenen dosyaları bulma ve sembolik bağlantılarla değiştirme


16

Yinelenen dosyalar (hatta farklı adlarla) için belirli bir dizin içinde kontrol etmek için bir yol bulmaya çalışıyorum ve bunları ilk kez gösteren semboller ile değiştirin. Ben denedim fdupesama sadece bu kopyaları listeler.
Bağlam bu: Beğendiğime göre bir simge temasını özelleştiriyorum ve birçok klasörün, üst klasörlerinde farklı adlara ve farklı konumlara sahip olmalarına ve farklı amaçlar için kullanılmalarına rağmen temelde aynı olduklarını gördüm resim. Aynı modifikasyonu yirmi ya da otuz kez uygulamak, sadece bir tane gerçekten gerekli olduğunda gereksiz olduğu için, sadece bir görüntü tutmak ve diğerlerini işaretlemek istiyorum.

Örnek olarak, eğer fdupes -r ./dizin içinde çalışırsam testdir, aşağıdaki sonuçları bana dönebilir:

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

Bu çıktı göz önüne alındığında, file1.pngtüm orijinal dosya adlarını korurken sadece dosyayı saklamak , diğerlerini silmek ve işaret eden sembollerle değiştirmek istiyorum. Böylece file2.pngadını koruyacak, ancak file1.pngkopya olmak yerine bir bağlantı haline gelecektir .

Bu bağlantılar mutlak bir yolu göstermemeli, üst testdirdizine göreli olmalıdır ; yani yetanotherfile.pngişaret edecek ../../file1.png, değil/home/testuser/.icons/testdir/file1.png

Hem GUI hem de CLI içeren çözümlerle ilgileniyorum. Bunu kullanmak zorunlu fdupesdeğil çünkü bildiğim bir araç ama diğer araçları kullanan çözümlere de açığım.

Tüm bunları işlemek için bir bash betiği oluşturmak o kadar zor olmamalıdır, ancak kendim nasıl yazacağımı öğrenecek kadar uzman değilim.

Yanıtlar:


3

İlk; Normal bağlantıları değil, sembol bağlantılarını kullanmanız için bir neden var mı? Göreli yollara sahip sembolik bağlara olan ihtiyacı anlamakta zorlanıyorum. Bu sorunu nasıl çözeceğim:

Bence fdupes Debian (Ubuntu) sürümü -Lseçeneğini kullanarak yinelenenleri sabit bağlantıları ile değiştirebilirsiniz , ama bunu doğrulamak için bir Debian yükleme yok.

-LSeçeneği olan bir sürümünüz yoksa, commandlinefu'da bulduğum bu küçük bash betiğini kullanabilirsiniz .
Bu sözdiziminin yalnızca bash'de çalışacağını unutmayın.

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

Yukarıdaki komut tüm yinelenen dosyaları "yol" da bulur ve bunları sabit bağlantılarla değiştirir. Bunu ls -ilRinode numarasını çalıştırarak ve bakarak doğrulayabilirsiniz . İşte on özdeş dosya içeren bir örnek:

$ 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üm dosyaların ayrı inode numaraları vardır, bu da onları ayrı dosyalar haline getirir. Şimdi bunları tekilleştirelim:

$ 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

Dosyaların hepsi aynı inode numarasına sahip, yani hepsi diskteki aynı fiziksel verilere işaret ediyor.

Umarım bu sorununuzu çözer veya en azından sizi doğru yöne yönlendirir!


Hatırladığımdan fdupes bağlantıları, @arnefm ile dupes değiştirmek için bir seçenek olan ama içinde bir şey göremiyorum adam ne de bir seçenektir v1.51(Ubuntu 14.04.2 LTS).
Alastair

Benim çatal jdupesde github.com/jbruchon/jdupes sahip -Lyinelenen setlerinin istenen sert bağlama yapar seçeneği.
Jody Lee Bruchon

Ben burada senaryoyu daha yeni ayarladım. Hala boşlukları işlemeyecek, ancak diğer özel karakterleri işleyecek (dosyalarda URL sorgu dizeleri vardı). Ayrıca, ${line//…/}parça benim için çalışma değildi, bu yüzden ilk "ana" dosyayı hardlink almak için daha temiz bir yol yaptım.
IBBoard

1
rsyncFarklı bir dosya sistemi kullanırsak göreceli yazılım bağlantılarına ihtiyacımız var mı? Veya dosya sistemi hiyerarşiyi korumuyorsa, örneğin her şeyi koyan bir yedekleme sunucusudur /«machine-name»/...? Veya yedekten geri yüklemek mi istiyorsunuz? Burada hardlinklerin nasıl korunacağını göremiyorum. Göreceli olarak, göreli yumuşak bağlantıların hayatta kalma şansı daha yüksektir.
Buddy

Softlinkler, dosyaları depoda tutarken bir git deposunda çoğaltırlarsa bir gereksinim olabilir.
BlakBat

6

Çok komut dosyası fantezi yoksa o zaman rdfind tavsiye ederim . Bu, verilen dizinleri yinelenen dosyalar için tarar ve bunları birbirine sabit veya yumuşak olarak bağlar. Ruby taşlar dizinimi tekilleştirme için büyük bir başarıyla kullandım. Debian / Ubuntu'da mevcuttur.


4

Benzer bir durum vardı, ama benim durumumda sembolik bağlantı göreceli bir yolu işaret etmelidir, bu yüzden hile yapmak için bu python komut dosyasını yazdım :

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

Her girdi satırı için (bir dosya listesi olan) komut dosyası dosya listesini böler (boşlukla ayrılmış), her dosyadan ilk dosyaya göreli yolu alır ve ardından sembolik bağlantıyı oluşturur.


1

Yani, arnefm tarafından verilen cevap (tüm internette kopyalanır) dosya adlarındaki boşluklarla ilgilenmez. Dosyalardaki boşluklarla ilgilenen bir senaryo yazdım.

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

Bu, dupes bulmak ve onları 'files' adlı bir dosyaya ayrılmış PIPE yazmaktır.

Daha sonra dosyayı bir satır satır satır bir diziye okur ve dizinin her öğesi PIPE tarafından sınırlanır.

Daha sonra, dizinin ilk olmayan tüm öğelerini yineleyerek dosyayı ilk öğeye bir sembolik işaretle değiştirir.

Harici dosya ('dosyalar') kaldırılabilir, eğer fdupes komutu bir alt kabukta yürütülürse, doğrudan süre tarafından okunur, ancak bu şekilde daha net görünür.


2
Bu sürümde, adında kanal içeren dosyalar var mı? Her iki sürümün de yeni satırlar içeren dosya adlarını işlemediğini varsayıyorum, ancak bu başka bir şey yerine fdupes sınırlamasıdır.
dhag

Öyle değil, ancak IFS'i istediğiniz gibi ayarlayabilirsiniz (ayrıca sed değiştirme içindeki değeri de değiştirebilirsiniz), o zaman herhangi bir sorun yaşamamalısınız (IFS 'ñ' ya da bunun gibi bir şey işe
yaramalıdır

Bu kırık sembolik bağlantılar oluşturur ve kendime bağlı dosyalar var. KULLANMAYIN
MrMesees

0

Bazı uyarılar önde:

  • BASH'a özgü
  • Dosya adlarında boşluk yok
  • Her satırda en fazla 2 dosya bulunduğunu varsayar.

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

2'den fazla dosya yineleniyorsa (örn. Dosya1 dosya2 dosya3) her çift için bir sembolik bağlantı oluşturmamız gerektiğinden - dosya1, dosya2 ve dosya1, dosya3'e 2 ayrı durum olarak davranın:

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

Bunu satır başına isteğe bağlı sayıda yinelenen sayıda otomatik olarak işlemek için harcamak biraz daha fazla çaba gerektirir.

Başka bir yaklaşım önce mutlak yollara sembolik bağlantılar oluşturmak, sonra bunları dönüştürmek olacaktır:

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 .

Bu, @Gilles tarafından verilen cevaba dayanmaktadır: /unix//a/100955/77319

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.