A probléma Szorgosan fotózunk. Idővel rákapunk arra is, hogy a fényképeinket szépen rendszerezett formában tároljuk valamelyik fotós workflow alkalmazás segítségével. A fotóink egy folder struktúrába importálódnak, példás rendben. Minden oké, amikor egyszer csak véletlenül egy nagy kupac régi képet kétszer húzunk be a libraryba – kész a baj, egy zsák fotónk lesz meg két példányban, feleslegesen. Lista kellene a duplikált file-jainkról!
A megoldás
Mielőtt alkalmazást keresnénk a feladat leküzdésére, agyalunk egy kicsit és kitaláljuk, hogy *nix-like környezetben menni fog ez külső segítség nélkül is. Két file egyezésének megállapításához pont elég, ha azok MD5 hash-e megegyezik. Írjunk hát egy scriptet, amely az egyező MD5 hash-ek alapján elkészít egy duplikált file listát:# A script a duplikált file-okat tartalmazó foldert várja paraméterként # és a jpg kiterjesztésű állományok egyezését vizsgálja csak. NAME="*.jpg" ALLFILES="/tmp/finddups.allfiles.tmp" DUPHASHES="/tmp/finddups.duphashes.tmp" find $1 -name "$NAME" -exec md5 {} \; | sort >$ALLFILES find $1 -name "$NAME" -exec md5 -q {} \; |sort|uniq -d|sort -n >$DUPHASHES while read hash do cat $ALLFILES | grep $hash | grep -oE '\((.*-[0-9].jpg)\)' | sed "s/(// ; s/)//" done <$DUPHASHES
find
-del kezdődő sor a célfolderben levő összes jpg file-unkból számol egy MD5 hash-t, majd a filelistát hash-estől leteszi egy file-ba. Ez a file valahogy így fog kinézni:
MD5 (./2000/05/15/20000515-0084.jpg) = 747888000b422d619a1b308346b7d81e MD5 (./2000/05/15/20000515-0086-2.jpg) = d3a064924fb8306519475b09cb90d66b MD5 (./2000/05/15/20000515-0086-3.jpg) = d3a064924fb8306519475b09cb90d66b MD5 (./2000/05/15/20000515-0086.jpg) = d3a064924fb8306519475b09cb90d66b
md5 -q
opciója), majd a uniq
binárissal csak a duplikált hash-eket szűrve (=-d opció) letesszük az összes, többször előforduló MD5 hasht egy másik file-ba.
Ezután már csak végig kell rohannunk a duplikált hash-eken, kikeresni, hogy mely file-okhoz tartoznak és kilistázni azokat – ezt teszi a script végi ciklus.
A ciklus közepén levő regexp arra hivatott, hogy az első körben előállított hash-es filelistából csak a filenevek jelenjenek meg – azok közül is csak azok a file-ok, amelyek neve -[0-9].jpg-re végződik.
Tudom, hogy a végén levő sed (amellyel a kibányászott filenevek elől és mögül lecsippentjük a zárójeleket) ágyúval verébre kategória. Nekem sem tetszik, gusztustalan – azonban szűk egy órán át nem találtam más megoldást, ezért maradt ilyen. Commentben lehet szárnyalni, ki hogyan választaná le így shellben az egrep által elkapott első capture group-ot.]]>
A második grep és a kis sed helyett egy sed:
sed ‘s/^[^(]*(\([^)]\+\).*/\1/’
Egy sed valóban szebb, de én egy szép grepet akarok 🙂 Azért beírtam egy pirosat!
grep -oE ‘[^(]*-[0-9].jpg’
# feltéve, hogy a fájlnévben nincs ‘(‘
vagy
grep -oE ‘\./.*-[0-9].jpg’
# ha a find által odarakott ‘./’-t biztos meghagyod később is
Eti, jár a sör, felírtam 🙂
Az én tippem: karaktertörlésre használj célszerszámot (KISS szabály):
tr -d ‘()’
KISS szabályból, illetve munkahelyi ártalomból fakadó további tipp: ne menj végig 2. a fájlokon a $DUPHASHES előállításához (rengeteg LASSÚ disk I/O kell hozzá), ha eleve ott van már minden infó, ami kell neked az ALLFILES-ben:
cat $ALLFILES | cut -d “=” -f 2 | sort -n | uniq -d | tr -d ‘ ‘ >$DUPHASHES
Wow, beírtam egy extralarge pirosat!-)
Ha ragaszkodsz a regexphez, akkor íme egy egyszerű megoldás, ami elfogad zárójelet a fájlnév közepében, de leszedi az első nyitót és utolsó csukót:
egrep -o ‘[^(].*[^)]’