help - shell szkript optimalizálása

sziasztok
nem nagyon értek a szkriptekhez, az alábbit tákoltam össze, de szeretném kérni, segítsetek, hogyan lehetne optimalizálni gyorsabb futásra. (gondolom, kevesebb "fork" meg pipe, stb.)
jelenleg a duplikátumok keresése (a while ciklusban) sokkal tovább tart, mint az md5 kalkulálása... ami azért elég gáz :)
ez volna az
előre is kösz a segítséget!

Hozzászólások

#
while read line; do
_SUM2=`echo "$line" | awk '{print $1}'`
_PATH2=`echo "$line" | awk '{print $2}'`
if [
.
.
.
done < email.sort

helyett

while read _SUM2 _PATH2;do
if [
.
.
.
done < email.sort

Ok. ez igaz - azt azért hozzáteszem, hogy az eredeti scriptben pont ugyanennyiszer hívja az rm-et (gyakorlatilag pont azt és úgy csinálja az awk, amit és ahogy a ciklus).
Az awk-ban a system() helyett { print $2 > "torolni" } és utána a torolni fájlt odaadni az xargs rm-nek. Meg persze az awk előtt illendően "> torolni" a bizronság kedvéért...

Igen "*." csere '*.' -ra jó meglátás, de a regexp annyiban jobb, hogy könnyebb továbbfejleszteni sha1sum-ra ;) Flexibilitás talán ennyit megér. Az md5sum annyiszor forkolódik csak amennyi paraméterrel egyszerre elbír, nem forkol minden file-ra kb mint az xargs.

Viszont nem esik kétségbe ha a file első karaktere - és attól sem ha szóköz vagy macskaköröm vagy aposztróf van a file nevében. (nem látható karakterekre pl backspace nem teszteltem :P)

Javított verzió:
find . -name '*.' -type f -exec md5sum -- {} + 2>/dev/null | sort | awk '{S1=$1; if(S1==S2) {gsub("^[[:xdigit:]]*[[:blank:]]*","");printf "%s\0",$0} else {S2=S1}}' | xargs -0 rm -f

jó, nekem ez már nem olvasható :) ennyire nem kell az utolsó századmásodpercet is lefaragnom :) inkább legyen picit lassabb, de könnyen emészthető :)
viszont tényleg nem értem, miért kell a *. -ot bármiféle idézőjelbe tenni? (kivéve ha mondjuk " *." fájnévről lenne szó, de nem)

Mert ha a futtató könyvtáradban vannak file-ok amikre teljesül a kifejezésed akkor azokat a shell szépen behelyettesíti neked a kifejezésed helyére és csak utána hívja meg a find-ot, de neked az nem lesz jó, meg a find-nak sem :) Ezért meg kell védened a kifejezésedet. Erre a legjobb az aposztróf de használhatnál visszafele perjelet (\) is így: \*.

Nem olyan bonyolult ez. Annyi baja azért van, hogy nem írja ki mennyi fájlt törölt, viszont amit letöröl azt gyorsan teszi. Ha az rm -f -et kicseréled ls -l -re akkor kipróbálhatod a sebességét.

Kicsit javítottam rajta:

find /mnt/data/cyrus -name '*.' -type f -exec md5sum -- {} + 2>/dev/null | sort | awk '{if($1==S) {gsub("^[[:xdigit:]]*[[:blank:]]*","");printf "%s\0",$0} else {S=$1}}' | xargs -0 rm -f

szóval hiába adok meg find-nak egy abszolút elérési utat, a cwd-t is beleveszi? ezt nem is tudtam. ez bug vagy feature? :)
más: ha esetleg csak simán generálnék egy fájl listát, egy sima rekurzív "ls"-el vagy valahogy, majd fájlméret szerint rendezném, keresném a dupliktumokat méret alapján, és csak ott végeznék md5sum-ot, ahol az azonos fájlméret ezt indokolja? az azért többet érne a fenti optimalizácóknál, nem?
mert most mondjuk 1 percig md5sum meg, majd 5 másodperc az összes többi művelet (mostmár... az első verziómban ez 2-3 perc volt! :) ), amit ti igyekeztek még jobban lefaragni, de az már nem számít, hogy most az 5 másodperc vagy 4,8 vagy akár 1 másodperc... (szerk.: ja mert azt nem mondtam, hogy a duplikátumok aránya az eddig a legrosszabb esetben olyan 70db volt saccperkábé 2000 fájlból. tehát most 2000 fájlon megy md5sum, pedig elég lenne azt a 70-et megnézni)
na meg is próbálom ezt a második verziót megvalósítani.

----------------------------------
feel the beat - it's everywhere!

Azt a "cwd"-t nem a find veszi bele, hanem még a find indulása előtt a shell dolgozza fel. A rekurzív ls-sel több baj is van, a legngyobb, hogy ott a listában nincs path, mert így néz ki a lista:


dir0:
név
név
név

dir1:
név
név
...

De tény, hogy az sokkal többet nyomna, ha le tudnád csökkenteni az md5 számolások darabszámát.

kőbaltás módszer:
[code]
find $_PATH -type f -name '*.' -exec du -b {} \; > email.list
sort email.list -n -o email.sort
[/cpde]

csakhogy maga az algoritmus nem működik. legalábbis ebben a formában, ahogy az eredeti megoldás, úgy nem. :(
ugye azonos fájlméretű email az lehet rengeteg, 10-20 is akár, nyugodtan. ezek tartalma még nem biztos, hogy egyezik. azonban ha a fenti módon sorbarendezem, majd keresem a duplikátumokat ugyanúgy, mint eddig, akkor mondjuk az 1. találatot és a 2. találatot összehasonlítja md5 alapján, majd kiderül, hogy azok különbözőek, és ekkor ugrik tovább. emgnézi a 2. és 3. találatokat. azok is különböznek. ettől azonban még a 3. és az 1. lehet azonos. igazából itt kéne még egy lista az azonos fájlméreteket mindent megnélzni md5 alapjén is, majd ezt a listát sorba rendezni, és úgy válogatni ki az azonos md5 fájlokat. na de ez már bonyolult... :)

Ha ugyanazt az elvet alkalmazod a fájl méretekre először amit az md5-ökre csináltunk akkor első körben kiszűröd az egyedi fájlmérettel rendelkezőket. Ezzel ezek md5 sumját megspórolod. Valahogy így:

find /mnt/data/cyrus -name '*.' -type f -exec stat -c "%s %n" {} + 2>/dev/null| sort -n | awk '{if ($1==S) {gsub("^[[:digit:]]*[[:blank:]]*","");printf "%s\0",$0} else {S=$1}}' | xargs -0 md5sum | sort | awk '{if($1==S) {gsub("^[[:xdigit:]]*[[:blank:]]*","");printf "%s\0",$0} else {S=$1}}' | xargs -0 rm -f

Ilyesmire gondoltál?

ez a szkripted hash-e, vagy mi? :D
fogalmam sincs, hogy erre gondoltam-e, mert ez nekem így kínai :D lehet, hogy menő dolog egy sorba mindent összehányni, de én írtam az elején is, hogy nem értek hozzá, úgyhogy nekem ez olvashatatlan.
én így módosítottam próbaképp, de ez így nem jó (az elv sem).
viszont most már bőven elégedett vagyok az eredménnyel, úgyhogy nagyon nem akarom már tovább faragni a dolgot, innentől kevesebb időt nyerek vele 10 év alatt, mint amennyit most ráfordítaniék :) (persze tanulásnak nem rossz)

Igazából nem is jó, most javítom :) Szkriptnek nagyon nem OK, de az elvet kipróbálni megfelelő egy ilyen egysoros is. Az elve az lenne, hogy azokat a fájlokat amelyeknek egyedi a mérete azonnal kivenné listából és csak olyan fájlokat vizsgálna tovább amelyekhez hasonló méretűek vannak a listában.

A javított verzió:

find /mnt/data/cyrus -name '*.' -type f -exec stat -c "%s %n" {} + 2>/dev/null| sort -n | awk 'BEGIN {L="";S=-1} {if($1==S) {gsub("^[[:digit:]]*[[:blank:]]*",""); printf "%s\0%s\0",L,$0;L=""} else {L=$0; gsub("^[[:digit:]]*[[:blank:]]*","",L)} ; S=$1;}' | xargs -0 md5sum | sort | awk '{if($1==S) {gsub("^[[:xdigit:]]*[[:blank:]]*","");printf "%s\0",$0} else {S=$1}}' | xargs -0 rm -f

Ha az rm -f -et ls -l -re cseréled és leméred az idejét akkor kb megtudod, hogy ez mennyit segítene a mintádon.

Olvashatóan ugyanez:


#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin
umask 077

find /mnt/data/cyrus -name '*.' -type f -exec stat -c "%s %n" {} + 2>/dev/null | sort -n | awk '
        BEGIN {
                L=""
                S=-1
        }
        {
                if($1==S) {
                        gsub("^[[:digit:]]*[[:blank:]]*","")
                        printf "%s\0%s\0",L,$0
                        L=""
                } else {
                        S=$1
                        gsub("^[[:digit:]]*[[:blank:]]*","")
                        L=$0
                }
        }' | xargs -0 md5sum | sort | awk '
        {
                if($1==S) {
                        gsub("^[[:xdigit:]]*[[:blank:]]*","")
                        printf "%s\0",$0
                } else {
                        S=$1
                }
        }' | xargs -0 echo rm -f

A find man lapját időnként érdemes elolvasni :

-exec command {} +
This variant of the -exec action runs the specified command on the selected files,
but the command line is built by appending each selected file name at the end; the
total number of invocations of the command will be much less than the number of
matched files. The command line is built in much the same way that xargs builds its
command lines. Only one instance of ‘{}’ is allowed within the command. The command
is executed in the starting directory.

aki a uniq parancs helyett ekkora böszmeséget irogat, az megérdemli...

Emlékeimben az van, hogy az awk tud asszociatív tömböket kezelni.
Tehát csinálsz benne egy tömböt, amiben a tömbindex az md5, az értéke meg simán 0 vagy 1, és amelyik tömbindex már benne van, az ahhoz tartozó path-t kiíród a kimenetre.

fordított aposztrófról javasolt leszokni, mert nem egymásbaágyazható és deprecated. $() a helyes.

Ha az md5 sum kimenetét pipe-olod a sortba, akkor egy fájllal kevesebb.

mit szeretnél tulajdonképpen? hash alapján összehasonlított duplikátumokat találni rekurzívan? én erre ezt írtam:


find ! -type d -print0 | xargs -0 md5sum | sort | uniq -D -w 32

hát itt ugye magából az md5sum-ból már relatív kevés példányt indítasz, kár hogy az viszont minden fájl hash-ét kiszámolja (tehát szerintem iszonyat lassú). Arról már nem is beszélve, hogy vajon mi a franc lehet ez a két uniq opció, mert én még az életben nem hallottam róluk (OK, uniq-ot azért nem használok naponta), de nem is szerepel a FreeBSD-s man-ban - azaz má megint valami gnuizm lehet.

nem cél hogy minden fájlra kiszámoljuk a hash-t? mert ha más a neve a fájlnak, de a tartalma ugyanaz, akkor is duplikátumnak veszem.

amúgy igen, kicsit gnu-s ez :)

man-ból:


       -D, --all-repeated[=delimit-method]
              print all duplicate lines delimit-method={none(default),prepend,separate}
              Delimiting is done with blank lines.

       -w, --check-chars=N
              compare no more than N characters in lines

ja, az más. akkor így csinálnám:


find ! -type d | xargs du -b | sort -n \
    | sed -r s/"\t"/"\t                 "/ \
    | uniq -D -w 16 | sed -r s/"^[0-9]*\t* *"/""/ \
    | xargs md5sum | sort | uniq -D -w 32

vagy \0 végződéssel:

find ! -type d -print0 | xargs -0 du -b | sort -n \
    | sed s/"\t"/"\t                 "/ \
    | uniq -D -w 16 | sed -r s/"^[0-9]*\t* *"/""/ \
    | tr -s "\n" "\0" | xargs -0 md5sum | sort | uniq -D -w 32

...így csak az egyforma nagyságú fájlokra fut le a hash check.

Mi lesz ha a file mérete nagyobb lesz mint 16 karakter?

Ha a du -b helyett stat -c "%s %n" -t használnál akkor az első sed feleslegessé is válna. Első xargs-ot pedig bele lehetne építeni a find-ba kb így:

Eredeti:


find ! -type d -print0 | xargs -0 du -b | sort -n | sed s/"\t"/"\t                 "/ |

Javaslatom:


find ! -type d -exec stat -c "%s                %n" {} + | sort -n |

Nem kell ahhoz, nagy diszk.


# ls -l /var/tmp/cucc
-rw-r--r-- 1 root root 2195802030080 May 28 14:53 /var/tmp/cucc

# ls -lh /var/tmp/cucc
-rw-r--r-- 1 root root 2.0T May 28 14:53 /var/tmp/cucc

# df -hP .
Filesystem            Size  Used Avail Use% Mounted on
/dev/vg/lv            7.8G  150M  7.3G   2% /var/tmp

De így is csak 13 számjegy.