Nylig har jeg behov for å slette mange duplikater. Jeg slår sammen tre eller fire filsystemer, og jeg vil at plassen skal brukes økonomisk. Først virket fdupes som det var det beste verktøyet for jobben, men jeg får stadig flere begrensninger.

Vurder kommandoen fdupes -rdN somedirectory/. Dette gjør en hash av alle filene i underkatalogene til en katalog.

Og når den støter på duplikater, sletter den dem, slik at det bare er en kopi av alt.

Men hva om jeg vil beholde somedirectory/subdirectory1/somefile og det er faktisk fire duplikater, og programmet møter en av duplikatene først? Så sletter den somedirectory/subdirectory1/somefile, som jeg ikke vil ha.

Jeg vil på en eller annen måte kunne spesifisere hvilke duplikater som skal beholdes. Og så langt er det ingen av standardprogrammene for håndtering av duplikater (duff, FSLint) ser ut til å tillate automatisering av den slags oppførsel. Jeg foretrekker ikke å rulle min egen, slik at jeg stiller dette spørsmålet.

Jeg vil gjerne kunne skrive noe sånt som

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

Kommentarer

Svar

Selv om funksjonaliteten du søker ikke er tilgjengelig på lager fdupes, gafflet jeg fdupes (gaffelen min heter jdupes) og la til noen funksjoner som kan løse dette problemet under visse omstendigheter. For eksempel, i det oppgitte tilfellet der du ønsker å beholde somedirectory/subdirectory1/somefile når automatisk sletting av duplikater (d og N byttes sammen) og det er ingen separate filer umiddelbart under somedirectory, jdupes kan mates hver umiddelbare underkatalogsti med subdirectory1 først og -O bryter (som sorterer filer etter kommandolinjeparameterrekkefølgen først):

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

Dette vil automatisk slette alle unntatt én fil i et duplikatsett, og vil garantere at hvis settet inneholder en fil i somedirectory/subdirectory1, vil den være den første og dermed automatisk bli den bevarte filen i settet . Det er fortsatt skarpe grenser for denne tilnærmingen, for eksempel det faktum at en annen kopi i somedirectory/subdirectory1 kan bevares i stedet for den du ønsket å beholde, men i mange tilfeller som din, jdupes parameterbestillingsalternativet som en løsning er bra nok.

I nær fremtid planlegger jeg å legge til et filtreringssystem til jdupes som vil muliggjøre en enorm grad av kontroll over inkludering / ekskludering av filer, bevaring for -N handlinger, og anvendelse av slike «filterstabler» på enten en global eller pr. -parametergrunnlag. Denne funksjonen er sårt nødvendig; Jeg ser for meg noe som dette for å «automatisk slette duplikater som ikke er null rekursivt, men alltid bevare somedirectory/subdirectory1/somefile som det er»:

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

Svar

Jeg så denne ikke noe annet sted: Si hva du vil er dette. Du har / mnt / folder-tree-1 / mnt / folder-tree-2. Du vil ikke fjerne hver dupe, men hvis en fil eksisterer i tree-2, og en identisk fil eksisterer i tree-1 med nøyaktig samme sti og navn, fjern det fra tre-2.

Advarsel: dette er ganske kort og hvis du prøver å kopiere og lime inn dette med begrensede skallferdigheter, vær forsiktig.

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 alt på en linje:

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 

Etterpå, inspiser og kjør rm-v2-dupes.sh

Svar

Hva med hardlinking av duplikatfilene sammen? På den måten brukes rommet bare en gang, men de eksisterer fortsatt i alle stier. Fangsten med dette er at hardlinkede filer skal modifiseres på plass (de skal bare modifiseres ved å slette filen og gjenskape den med det nye innholdet). Den andre tilnærmingen er å koble filene sammen, selv om du har det samme problemet med å bestemme hvilken «primære» fil. Dette kan gjøres med følgende skript (selv om det ikke håndteres filnavn som inneholder mellomrom).

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

Kommentarer

  • Bruker jdupes i stedet for fdupes du kan ganske enkelt gå jdupes -nrL somedirectory/ som er enormt raskere.
  • Skrivefeil i lenken til jdupes. Bekvemmelighetslink: github.com/jbruchon/jdupes

Svar

Jeg hadde det samme spørsmålet.Hvis du har mange duplikater, holder fdupes /my/directory/ -rdN filen med den eldste modifikasjonsdatoen, eller hvis flere filer har samme modifikasjonsdato, er den som ble funnet først.

Hvis endringsdatoen ikke er viktig for deg, kan du touch filene i katalogen du vil beholde. Hvis du velger å touch dem med gjeldende dato og klokkeslett, vil fdupes -rdNi beholde de med gjeldende dato. Eller du kan touch beholde filene med en dato som er tidligere enn den du vil slette, og bruke fdupes -rdN som normalt.

Hvis du trenger å beholde endringsdatoen, må du bruke en av de andre metodene.

Svar

Bare for å legge til en vri på et tidligere svar. Jeg har brukt følgende kode flere ganger og endret litt på et tidligere svar med en enkel | grep for å isolere mappen jeg vil slette fra.

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

Igjen, dette vil opprette en sh-fil for å slette alle filene som er oppført, ingen kommenterte linjer. Selvfølgelig kan du fremdeles redigere filen for å kommentere spesifikke linjer / filer du vil beholde.

Et annet hint for store kataloger er å kjøre fdupes til en txt-fil og deretter eksperimentere med | grep og | sed til jeg får resultat jeg vil 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

Bruk sed for å opprette en skallfil som inneholder kommandoer som er kommentert for å slette hver av dine dupliserte filer:

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

Den resulterende remove-duplicate-files.sh fil som vi nettopp har opprettet, vil hver linje kommenteres. Fjern merking av filene du vil slette. Kjør deretter sh remove-duplicate-files.sh . Voila!

OPPDATERING

Vel, hvis du ikke bare vil slette filer i visse kataloger, er det så enkelt som dette :

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 

Hvor exclude_duplicates.py er:

 #/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 resulterende remove-duplicate-files-keep-protected.sh -fil som vi nettopp har opprettet, vil kommentere alle filer fra beskyttede kataloger. Åpne denne filen i teksteditoren din, sjekk at alt er i orden. Kjør den deretter. Voila (sic)!

Kommentarer

  • jeg tenkte på dette, men det ‘ er ikke automatisert nok. Dumt, jeg forårsaket tap av data med denne metoden når håndtere duplikater fordelt på flere filsystemer … der ‘ er det ingen måte å tildele en prioritet, gitt utdata fra fdup es. i utgangspunktet hadde jeg hatt å tråle gjennom 10000 filer for hånd for å forhindre at tap av data … så nei takk … faktisk, at tap av data er nettopp grunnen til at jeg stilte dette spørsmålet. > @ixtmixilix, vel, manuell metode er avhengig av brukerens oppmerksomhet, her er ‘ ikke noe nytt. Hvis du vil ha noe mer automatisert, kan du sjekke et oppdatert svar ovenfor.

Svar

Hva med noe sånt?

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

Fra og med Apple Filesystem (APFS), en annen løsning er å beholde filene, deduplisere dem og ikke ha noen innvirkning på diskbruk. Se Erstatt eksisterende dupliserte filer på APFS med kloner

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *