Nyligen har jag behov av att ta bort många dubbletter. Jag slår ihop tre eller fyra filsystem och vill att utrymmet ska användas ekonomiskt. Först verkade fdupes det var det bästa verktyget för jobbet, men jag får alltmer begränsningar.

Tänk på kommandot fdupes -rdN somedirectory/. Detta gör en hash av alla filer i underkatalogerna i somedirectory.

Och när det stöter på dubbletter raderas det så att det bara finns en kopia av allt.

Men tänk om jag vill behålla somedirectory/subdirectory1/somefile och det finns faktiskt fyra dubbletter, och programmet möter en av dubbletterna först? Sedan raderas somedirectory/subdirectory1/somefile, som jag inte vill ha.

Jag vill på något sätt kunna ange vilka dubbletter som ska behållas. Och hittills ingen av standardprogrammen för hantering av duplikat (duff, FSLint) verkar möjliggöra automatisering av den typen av beteende. Jag föredrar att inte rulla min egen, så det är därför jag ställer den här frågan.

Jag skulle vilja kunna skriva något som

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/ 

Kommentarer

Svar

Medan den funktionalitet du söker inte finns i lager fdupes, gafflade jag fdupes (min gaffel heter jdupes) och lade till några funktioner som kan lösa problemet under vissa omständigheter. exempel, i det angivna fallet där du vill behålla somedirectory/subdirectory1/somefile vid automatisk radering av dubbletter (d och N växlar tillsammans) och det finns inga separata filer direkt under somedirectory, jdupes kan matas varje omedelbar underkatalogväg med subdirectory1 först och -O -omkopplare (som sorterar filer efter kommandoradsparameterordning först):

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

Detta kommer automatiskt att ta bort alla utom en fil i en dubblettuppsättning och garanterar att om uppsättningen innehåller en fil i somedirectory/subdirectory1 blir den den första och därmed automatiskt blir den bevarade filen i uppsättningen . Det finns fortfarande uppenbara gränser för detta tillvägagångssätt, till exempel det faktum att en annan kopia i somedirectory/subdirectory1 kan bevaras istället för den du ville behålla, men i ett stort antal fall som din, jdupes parameterordningens alternativ som lösning är tillräckligt bra.

I en nära framtid planerar jag att lägga till ett filtreringssystem till jdupes som möjliggör en enorm kontroll över inkludering / uteslutning av filer, bevarande för -N åtgärder och tillämpning av sådana ”filterstackar” på antingen en global eller per -parameterbasis. Denna funktion är mycket nödvändig; Jag tänker mig något så här för att ”automatiskt radera icke-noll duplikat rekursivt MEN alltid behålla somedirectory/subdirectory1/somefile som det är”:

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

Svar

Jag såg inte den här någon annanstans: Säg vad du vill är det här. Du har / mnt / mapp-träd-1 / mnt / mapp-träd-2. Du vill inte ta bort varje dupe, men om en fil finns i träd-2, och en identisk fil finns i träd-1 med exakt samma sökväg och namn, ta bort det från träd-2.

Varning: detta är ganska kortfattat och om du försöker kopiera och klistra in det med begränsade skalfärdigheter, var försiktig.

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 

Eller allt på en rad:

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 

Efteråt, inspektera och kör rm-v2-dupes.sh

Svar

Hur är det med att länka de dubbla filerna samman? På det sättet används utrymmet bara en gång, men de finns fortfarande i alla vägar. Fångsten med detta är att hårdlänkade filer ska modifieras på plats (de bör endast modifieras om du tar bort filen och återskapar den med det nya innehållet). Det andra tillvägagångssättet är att länka samman filerna, även om du har samma fråga om att bestämma vilken ”primära” fil. Detta kan göras med följande skript (även om det inte hanterar filnamn som innehåller mellanslag).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do for DEST in $DESTS; do ln -f $SOURCE $DEST done done 

Kommentarer

  • Använd jdupes istället för fdupes du kan helt enkelt gå jdupes -nrL somedirectory/ vilket är enormt snabbare.
  • Skrivfel i länken till jdupes. Bekvämlänk: github.com/jbruchon/jdupes

Svar

Jag hade samma fråga.Om du har många dubbletter fdupes /my/directory/ -rdN behåller filen det äldsta modifieringsdatumet, eller om flera filer har samma modifieringsdatum, så hittas den först.

Om ändringsdatumet inte är viktigt för dig kan du touch filerna i katalogen du vill behålla. Om du väljer att touch dem med aktuellt datum och tid kommer fdupes -rdNi att behålla dem med det aktuella datumet. Eller så kan du touch behålla filerna med ett datum som är tidigare än det du vill ta bort och använda fdupes -rdN som vanligt.

Om du behöver behålla ändringsdatumet måste du använda någon av de andra metoderna.

Svar

Bara för att lägga till en twist till ett tidigare svar. Jag har använt följande kod flera gånger och ändrat något tidigare svar med en enkel | grep för att isolera mappen jag vill ta bort från.

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

Återigen kommer detta att skapa en sh-fil för att radera alla listade filer, inga kommenterade rader. Naturligtvis kan du ändå redigera filen för att kommentera specifika rader / filer du vill behålla.

En annan ledtråd för stora kataloger är att köra fdupes till en txt-fil och sedan experimentera med | grep och | sed tills jag får resultat jag vill ha.

`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` 

Svar

Använd sed för att skapa en skalfil som innehåller kommandon som har kommenterats för att ta bort var och en av dina dubbletter av filer:

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

Den resulterande remove-duplicate-files.sh fil som vi just har skapat kommer varje rad att kommenteras. Avmarkera de filer du vill ta bort. Kör sedan sh remove-duplicate-files.sh . Voila!

UPPDATERING

Om du inte bara vill ta bort filer i vissa kataloger är det så enkelt som det här :

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 

Där exclude_duplicates.py är:

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

Den resulterande remove-duplicate-files-keep-protected.sh -fil som vi just skapat kommer att kommentera alla filer från skyddade kataloger. Öppna den här filen i din favorittextredigerare, kontrollera att allt är OK. Kör sedan. Voila (sic)!

Kommentarer

  • Jag tänkte på detta, men det ’ är inte automatiskt nog. Dumt, jag orsakade dataförlust med den här metoden när hantera duplikat fördelade över flera filsystem … där ’ är inget sätt att tilldela en prioritet, med tanke på utdata från fdup es. i grund och botten skulle jag ha trålt 10000 filer för hand för att förhindra att dataförlust … så, nej tack … faktiskt, att dataförlust är just anledningen till att jag ställde den här frågan.
  • @ixtmixilix, ja, manuell metod är beroende av användarnas uppmärksamhet, här är ’ inget nytt. Om du vill ha något mer automatiserat, kolla in ett uppdaterat svar ovan.

Svar

Vad sägs om något liknande?

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

Svar

Från och med Apple Filesystem (APFS), en annan lösning är att behålla filerna, deduplicera dem och inte ha någon inverkan på diskanvändningen. Se Ersätt befintliga dubbla filer på APFS med kloner

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *