Работу над программой начнём объявления ассоциативного массива
%collectedFiles
и с копирования определения процедуры
compareFiles
из нашей программы cmp.pl:
Perlmy %collectedFiles; sub compareFiles($$;$) { … }
Объявление и определение процедуры find
мы тоже
позаимствуем из программы find.pl, но с небольшими
изменениями:
Perlsub find($); sub find($) { my $name=shift; if(-d $name) { … (удаляемprint "$name\n";
) … } elsif(-f $name) { push @{$collectedFiles{-s $name}->[0]}, $name; (вместоprint "$name\n";
) } else { … } }
Смысл этих изменений заключается в следующем. Поскольку никакая обработка
найденных директорий нам не требуется (мы собираемся сравнивать лишь файлы), мы
удалили первую команду print
. Файлам же нужна особая
обработка. Расшифруем не совсем очевидную строчку, начинающуюся с push
. Выражение $collectedFiles{-s
$name}
— это гнездо файлов размера -s
$name
, то есть список групп (ссылка на анонимный массив). В этом списке
пока лишь одна группа, $collectedFiles{-s
$name}->[0]
(она тоже является ссылкой на анонимный массив).
Выражение @{$collectedFiles{-s $name}->[0]}
даёт сам массив, на который указывает ссылка. И в него-то мы и добавляем имя
файла с помощью push
.
Выполнение программы начнётся с вызова процедуры findDups
,
который мы сразу помещаем в самый конец файла. Всё, что указано в командной
строке программы, сразу передаётся процедуре:
PerlfindDups(@ARGV);
Займёмся её программированием:
Perlsub findDups(@) { push @_, '.' unless @_; работаем по умолчанию с текущей директорией find($_) for @_; заполняем%collectedFiles
for my $size(sort { $a<=>$b } keys %collectedFiles) перебираем всевозможные размеры файлов (по возрастанию) — это ключи в%collectedFiles
{ my $groups=$collectedFiles{$size}; получаем список групп для данного размера for(my $g=0; $g<@$groups; $g++) перебираем номера групп в списке { my $group=$groups->[$g]; берём группу с номером$g
и обрабатываем её: for(my $i=1; $i<@$group; $i++) { push @{$groups->[$g+1]}, splice @$group, $i--, 1 if compareFiles($group->[0], $group->[$i]); } if(@$group>1) { print "* Размер: $size\n"; print "$_\n" for sort @$group; print "\n"; } } } }
Обработка группы номер $g
заключается в переборе всех её
элементов, начиная со второго, сравнении их с первым, и, если обнаружено
различие (… if compareFiles($group->[0],
$group->[$i])
), в переброске такого элемента в следующую, $g+1
-ю группу. Разберём операцию переброски подробнее.
Выражение splice @$group, $i--, 1
удаляет
$i
-й элемент из группы. Поскольку на место удалённого
элемента встанет следующий (если он есть), необходимо уменьшить счётчик
элементов $i
, чтобы компенсировать его автоматическое
увеличение в цикле; без этого элемент, следующий после удалённого, будет
пропущен без сравнения с первым. Выражение splice
…
возвращает удалённое имя файла, которое тут же добавляется в $g+1
-ю группу командой push
@{$groups->[$g+1]}, …
. Затем группы, включающие более одного имени
файла, выводятся на экран с указанием размера файлов в группе.