Finden Sie doppelte Dateien und ersetzen Sie sie durch symbolische Links

16

Ich versuche eine Möglichkeit zu finden, in einem bestimmten Verzeichnis nach doppelten Dateien (auch mit unterschiedlichen Namen) zu suchen und diese durch Symlinks zu ersetzen, die auf das erste Vorkommen verweisen. Ich habe es mit versucht, fdupesaber es listet nur diese Duplikate auf.
Das ist der Kontext: Ich passe ein Symbolthema nach meinen Wünschen an, und ich habe festgestellt, dass viele Symbole, auch wenn sie unterschiedliche Namen und unterschiedliche Speicherorte in ihrem übergeordneten Ordner haben und für unterschiedliche Zwecke verwendet werden, im Grunde gleich sind Bild. Da es überflüssig ist, dieselbe Änderung zwanzig- oder dreissigmal anzuwenden, wenn nur eine wirklich notwendig ist, möchte ich nur ein Bild behalten und alle anderen verknüpfen.

Wenn ich beispielsweise fdupes -r ./in dem Verzeichnis ausgeführt werde testdir, werden möglicherweise die folgenden Ergebnisse zurückgegeben:

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

Angesichts dieser Ausgabe möchte ich nur die Datei behalten file1.png, alle anderen löschen und durch darauf verweisende Symlinks ersetzen, wobei alle ursprünglichen Dateinamen beibehalten werden. So file2.pngbleibt der Name erhalten, wird aber zu einem Link, file1.pnganstatt ein Duplikat zu sein.

Diese Links sollten nicht auf einen absoluten Pfad verweisen, sondern relativ zum übergeordneten testdirVerzeichnis sein. dh yetanotherfile.pngwird zeigen auf ../../file1.png, nicht auf/home/testuser/.icons/testdir/file1.png

Ich interessiere mich sowohl für Lösungen, die eine GUI und CLI beinhalten. Es ist nicht zwingend verwenden fdupesich es genannt habe , weil es ein Werkzeug ist , dass ich weiß, aber ich bin offen für Lösungen , die anderen Werkzeuge auch nutzen.

Ich bin mir ziemlich sicher, dass ein Bash-Skript für all das nicht so schwer zu erstellen sein sollte, aber ich bin nicht sachkundig genug, um herauszufinden, wie man es selbst schreibt.

Sekhemty
quelle

Antworten:

3

Zuerst; Gibt es einen Grund, warum Sie Symlinks verwenden müssen und nicht die üblichen Hardlinks? Es fällt mir schwer, die Notwendigkeit von Symlinks mit relativen Pfaden zu verstehen. So würde ich dieses Problem lösen:

Ich denke, dass die Debian (Ubuntu) -Version von fdupes Duplikate durch feste Links ersetzen kann -L, aber ich habe keine Debian-Installation, um dies zu überprüfen.

Wenn Sie keine Version mit dieser -LOption haben, können Sie dieses kleine Bash-Skript verwenden, das ich auf commandlinefu gefunden habe .
Beachten Sie, dass diese Syntax nur in Bash funktioniert.

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

Der obige Befehl findet alle doppelten Dateien im "Pfad" und ersetzt sie durch Hardlinks. Sie können dies überprüfen, indem Sie ls -ilRdie Inode-Nummer ausführen und überprüfen . Hier ist ein Beispiel mit zehn identischen Dateien:

$ 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

Alle Dateien haben separate Inode-Nummern, sodass sie separate Dateien sind. Nun wollen wir sie deduplizieren:

$ 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

Die Dateien haben jetzt alle die gleiche Inode-Nummer, was bedeutet, dass sie alle auf die gleichen physischen Daten auf der Festplatte verweisen.

Ich hoffe das löst dein Problem oder weist dich zumindest in die richtige Richtung!

Arnefm
quelle
Ich erinnere mich, dass fdupes eine Option hat, um dupes durch Links zu ersetzen, @arnefm, aber ich kann nichts in dem Mann sehen, noch ist es eine Option in v1.51(Ubuntu 14.04.2 LTS).
Alastair
Mein Fork jdupesunter github.com/jbruchon/jdupes hat die -LOption, das gewünschte Hardlinking von Duplikatsätzen durchzuführen.
Jody Lee Bruchon
Ich habe gerade das Skript hier optimiert. Leerzeichen werden immer noch nicht verarbeitet, andere Sonderzeichen werden jedoch verarbeitet (ich hatte URL-Abfragezeichenfolgen in Dateien). Außerdem ${line//…/}funktionierte der Teil nicht für mich, so dass ich einen saubereren Weg ging, um die erste "Master" -Datei auf Hardlink zu bringen.
IBBoard
1
Benötigen wir relative Softlinks, wenn wir rsyncein anderes Dateisystem verwenden? Oder wenn das Dateisystem die Hierarchie nicht beibehält, z. B. ein Sicherungsserver, der alles unterbringt /«machine-name»/...? Oder wenn Sie aus dem Backup wiederherstellen möchten? Ich kann nicht sehen, wie Hardlinks hier erhalten bleiben. Relative Softlinks hätten wahrscheinlich eine bessere Überlebenschance.
Buddy
6

Wenn Sie nicht viel an Skripten interessiert sind, kann ich rdfind empfehlen . Damit werden die angegebenen Verzeichnisse nach doppelten Dateien durchsucht und entweder per Hard- oder Softlink miteinander verknüpft. Ich habe es zum Deduplizieren meines Ruby-Edelsteinverzeichnisses mit großem Erfolg verwendet. Es ist in Debian / Ubuntu verfügbar.

Andrew Frankreich
quelle
4

Ich hatte eine ähnliche Situation, aber in meinem Fall sollte der symbolische Link auf einen relativen Pfad verweisen, also habe ich dieses Python-Skript geschrieben , um den Trick auszuführen:

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

Für jede Eingabezeile (die eine Liste von Dateien ist) teilt das Skript die Dateiliste (durch Leerzeichen getrennt), ruft den relativen Pfad von jeder Datei zur ersten ab und erstellt dann den Symlink.

filipenf
quelle
1

Die Antwort von arnefm (die über das Internet kopiert wurde) behandelt also keine Leerzeichen in Dateinamen. Ich habe ein Skript geschrieben, das sich mit Leerzeichen in Dateien befasst.

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

Was dies bedeutet, ist Dupes zu finden und sie PIPE getrennt in eine Datei mit dem Namen "files" zu schreiben.

Anschließend wird die Datei zeilenweise in ein Array zurückgelesen, und jedes Element des Arrays wird durch die PIPE begrenzt.

Anschließend werden alle nicht ersten Elemente des Arrays durchlaufen, wobei die Datei durch einen Symlink zum ersten Element ersetzt wird.

Die externe Datei ('files') könnte entfernt werden, wenn der Befehl fdupes in einer Subshell ausgeführt wird, die von der Zeit direkt gelesen wird, aber dieser Weg scheint klarer zu sein.

David Ventura
quelle
2
Handelt es sich bei dieser Version um Dateien mit Namen, die eine Pipe enthalten? Ich gehe davon aus, dass keine der Versionen Dateinamen mit Zeilenumbrüchen behandelt, aber das ist eher eine Einschränkung von fdupes als von anderen.
Dhag
Es funktioniert nicht, aber Sie können IFS auf das einstellen, was Sie wollen (auch den Wert im sed-Ersatz ändern), dann sollten Sie kein Problem haben (IFS auf 'ñ' oder so ähnlich sollte funktionieren)
David Ventura
Dadurch entstehen fehlerhafte Symlinks, und ich habe Dateien mit sich selbst verknüpft. NICHT
VERWENDEN
0

Einige Vorbehalte vorab:

  • BASH-spezifisch
  • Kein Leerzeichen in Dateinamen
  • Angenommen, jede Zeile enthält höchstens 2 Dateien.

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

Wenn mehr als 2 Dateien Duplikate sind (z. B. Datei1 Datei2 Datei3), müssen wir für jedes Paar einen Symlink erstellen. Behandeln Sie Datei1, Datei2 und Datei1, Datei3 als zwei separate Fälle:

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

Wenn Sie dies tun, um automatisch eine beliebige Anzahl von Duplikaten pro Zeile zu verarbeiten, ist der Aufwand etwas höher.

Ein anderer Ansatz wäre, zuerst Symlinks zu absoluten Pfaden zu erstellen und diese dann zu konvertieren:

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 .

Dies basiert auf der Antwort von @Gilles: /unix//a/100955/77319

Dani_l
quelle