LINUX.ORG.RU

как улучшить ускорить скрипт?


0

2

привет всем.

есть задача уменьшить место на диске. файлы не изменяемые. файлы лежат на одном разделе. значит можно попытаться сделать hard ссылки на одинаковые файлы.

на скорую руку сделал такой скрипт:

  • перебираю файлы, подсчитываю sha1:
  • fec6d770fdacda2a3c382ce0545d96623a42aa53 имя/файла
  • потом хеш использую как имя файла .objects/fe/c6d770fdacda2a3c382ce0545d96623a42aa53
  • если нет файла с именем .objects/fe/c6d770fdacda2a3c382ce0545d96623a42aa53 , то делаю его ссылкой на файл, есть он есть, то делаю ссылку наоборот.
  • в конце всего этого удаляю каталог .objects

в итоге все одинаковые файлы будут ссылаться на один и тот же дескриптор.

сам скрипт:

#!/bin/bash 

if [ -d .objects ]
then
        rm -Rf .objects
fi

for file in $( find -xdev -type f )
do
        sha=$( sha1sum $file | sed 's/^\(..\)/.objects\/\1\//' )
        obj_name=$( echo $sha | cut -f1 -d ' ' )
        file_name=$( echo $sha | cut -f2 -d ' ' )
        dir_name=$( echo $sha | cut -c-12 )
        mkdir -p "$dir_name"
        if [ -f "$obj_name" ]
        then
                ln -f "$obj_name" "$file_name"
        else
                ln -f "$file_name" "$obj_name"
        fi
done

if [ -d .objects ]
then
        rm -Rf .objects
fi

ps. sh скрипты никогда не писал, поэтому и спрашиваю чего тут может быть неправильно и как его улучшить...

самое ресурсоемкое здесь это вычисление хэша, так что заметного ускорения ему сделать не удастся (только если перейти на хэш попроще)

anonymous
()

КМК, тут ещё затраты на операции с файлами идут (создание этих твоих файлов в .objects). Если делать то же самое в памяти, то можно ускорить процесс, наверное.

Obey-Kun ★★★★★
()

вообще то хеши могут совпадать у разных файлов, просто сравнения хешей недостаточно. Ещё можно взять lessfs, как раз делает примерно что тебе нужно.

mashina ★★★★★
()

надо понять во что упёрлась производительность. Посмотри iostat -x -m 3 на сколько винты загружены. Если на 100% то можно дальше оптимизацией не париться :).

А так типичная проблема bash-скриптов в том что каждое действие это форк и запуск комманды. Поэтому тупо переписав на <твой любимый ЯП> можно сильно ускорить работу.

true_admin ★★★★★
()

Я для подобных целей сделал вот такой скриптик:

#!/bin/sh
#	2 минуты 12.38 секунд  на 12'989 файлов общим размером 10'026'786'125 байт
#		(стало: 9'919'093'813 байт)
#	1 минута 13.00 секунд на 10'399 файлов общим размером 27'171'656'169 байт
#		(стало: 26'401'660'287 байт)
#	1 минута 17.82 секунд на 8'686 файлов общим размером 11'474'216'791 байт
#		(стало: 11'131'537'062 байт)
#	3 минуты 19.77 секунд на 16'257 файлов общим размером 70'697'892'519 байт
#		(стало: 69'132'667'051 байт)
#
FILELIST="filelist_4_mysql"
SQLFILE="tmp_4_mysql"
OUTP="double_files"

STEP_CNTR=0
#DUP_CNTR=0

function Step(){
	STEP_CNTR=$[$STEP_CNTR + 1 ]
	echo -e "\n\e[1;32m$STEP_CNTR\t\t$*...\e[0m"
}

alias mysql='mysql --default-character-set=koi8r --batch -s files_db'
rm -f $FILELIST $SQLFILE $OUTP

Step "Making list of files"
find  -type f -printf "%p\t%s\n" | iconv -f koi8-r -t utf8 > $FILELIST


Step "Finding files with same size"
cat > $SQLFILE << EOF
delete from files;
load data local infile "filelist_4_mysql" into table files(filename, filesize);
delete from files where filesize in (select filesize from (select filesize,count(*) c from files group by filesize having c = 1) T);
delete from files where filesize = 0;
select filesize from files group by filesize;
EOF

mysql < $SQLFILE > $OUTP

cat > $SQLFILE << EOF
delete from dups;
load data local infile "filelist_4_mysql" into table dups(filename, filemd5);
delete from dups where filemd5 in (select filemd5 from (select filemd5,count(*) c from dups group by filemd5 having c = 1) T);
select filename from dups group by filemd5;
EOF

Step "Finding duplicates"
while read SIZE
do
	rm -f $FILELIST
	echo "select filename from files where filesize = $SIZE ;" | mysql | while read FILE
	do
		MD=$(md5sum "$FILE" 2>/dev/null);
		if [ "$MD" != "" ]; then
			echo -e "$FILE\t$MD" | iconv -f koi8-r -t utf8 >> $FILELIST 
		else
			echo -e "\e[1;31;40mCant read MD5 of $FILE\e[0m\nTrace:"
			echo "select filename from files where filesize = $SIZE;" | mysql
		fi
	done
	mysql < $SQLFILE | while read FILE
	do
		echo -e "\n\e[1;41;33m$FILE\e[36m has dublicates:\e[0m"
		echo "select filename from dups where filemd5 = (select filemd5 from dups where filename = \"$FILE\") AND filename != \"$FILE\";" | mysql  | while read D_FILE
		do
			echo -e "\e[1;32;40m$D_FILE\e[0m"
			[ "$1" = "-d" ] && rm -f "$D_FILE" && echo "deleted"
			[ "$1" = "-l" ] && ln -f "$FILE" "$D_FILE" && echo "linked"   #|| ln -fs "$FILE" "$D_FILE" || echo -e "\e[1;31;40merror linking $FILE to $D_FILE!!!\e[0m"
		done
	done
done < $OUTP

Step "Deleting trash"
echo "delete from files; delete from dups;" | mysql
rm -f $FILELIST $SQLFILE $OUTP
Для упрощения хранения данных использую mysql.

P.S. А еще существует готовая программка для поиска дубликатов, не помню, правда, как называется. Ее время поиска чуть ли не в 2-3 раза быстрее моего скриптика, но чем-то она мне в свое время не понравилась, что я такую страшиздилу накатал...

Eddy_Em ☆☆☆☆☆
()

Хеши имеет смысл сравнивать только у файлов с одинаковым размером.

Olegymous ★★★
()
Ответ на: комментарий от Eddy_Em

> P.S. А еще существует готовая программка для поиска дубликатов, не помню, правда, как называется.

fdupes, rdfind

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Точно, fdupes я и пробовал. Но чего-то у нее не так было...

Eddy_Em ☆☆☆☆☆
()

zfs + deduplication :-)

anonymous
()

Кривой скрипт

Твой скрипт обломается сразу же при файлах/папках имеющих в названиях пробелы, или имеющих в названиях в начале символ "-".

someloruser
()
Ответ на: комментарий от Led

даааа... я ламер... :-)

спасибо за «man 1 hardlink»

denis_ka
() автор топика
Ответ на: комментарий от denis_ka

не экранирую имя файла при вычислении sha1sum?

yes.

«for file in» заменить на

TEMPLIST=`mktemp /tmp/tfile.XXXXXX`
find -xdev -type f > ${TEMPLIST}

while read file
do

    ...

done < ${TEMPLIST}

rm -- ${TEMPLIST}

cut-ы переделать.

Возможно понадобится в этом

ln -f "$obj_name" "$file_name"
ln -f "$file_name" "$obj_name"
добавить "--"
ln -f -- "$obj_name" "$file_name"
ln -f -- "$file_name" "$obj_name"

someloruser
()
Ответ на: комментарий от someloruser

cut не знаю на что заменить, expr substr кажись не умеет вырезать с символа до конца, ${string:2:2} - а это только bash?

а зачем — для ln?

denis_ka
() автор топика
Ответ на: комментарий от denis_ka
bash-3.1$ touch -- -abc
bash-3.1$ ls -l
-rw-rw---- 1 star star 0 2011-05-10 00:28 -abc
bash-3.1$ ln -s -abc zzz
ln: invalid option -- '1'
Try `ln --help' for more information.
bash-3.1$
bash-3.1$ ln -s -- -abc zzz
bash-3.1$ ls
-abc  zzz
bash-3.1$ ls -l
total 0
-rw-rw---- 1 star star 0 2011-05-10 00:28 -abc
lrwxrwxrwx 1 star star 4 2011-05-10 00:28 zzz -> -abc

Если первое имя файла будет начинаться с знака "-", то будет ошибка.

someloruser
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.