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
- jag var letade efter samma sak och jag hittade det här superanvändare.com/a/561207/218922
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örfdupes
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