Как найти дубликаты файлов, сравнив их по размеру (ie: без хеширования) в bash - PullRequest
0 голосов
/ 04 мая 2020

Как найти дубликаты файлов путем сравнения их по размеру (ie: без хэширования) в bash.

Файлы тестового стенда:

-rw-r--r--   1 usern  users  68239 May  3 12:29 The W.pdf
-rw-r--r--   1 usern  users  68239 May  3 12:29 W.pdf
-rw-r--r--   1 usern  users      8 May  3 13:43 X.pdf

Да, файлы могут иметь пробелы (Boo!).

Я хочу проверить файлы в том же каталоге, переместить файлы, которые соответствуют чему-то другому, в папку «это, вероятно, дубликаты».

Мой вероятный вариант использования: люди будут случайным образом неправильно называть меньший набор файлов (ie: не создавать файлы произвольной длины). Маловероятно, что два файла будут одинакового размера, но будут разными файлами. Конечно, в качестве резервной копии я мог иметь sh и проверить два файла одинакового размера. Но в основном это будут люди, которые берут файл и называют его неназванным / повторно добавляют его в кучу, которой он уже есть.

Итак, желательно решение с широко установленными инструментами (posix?). И я не должен анализировать вывод ls, поэтому мне нужен другой способ получить фактический размер (а не du приблизительный).

"Проголосовать, чтобы закрыть!"

Задержите ковбоя.

Могу поспорить, вы собираетесь предложить это (круто, вы можете поискать в Google):

https://unix.stackexchange.com/questions/71176/find-duplicate-files

Нет fdupes (ни jdupes, ни ...), ни finddup, ни rmlint, ни fslint - я не могу гарантировать это в других системах (намного меньше) мое), и я не хочу застрять в качестве службы поддержки клиентов, занимающихся их установкой на случайных системах с настоящего времени до вечности, и даже не получая электронные письма об этом sh ... материале и необходимости сообщать их RTFM и вычислять это из. Кроме того, на самом деле я должен написать свой сценарий для проверки функциональности установленного, но это выходит за рамки.

https://unix.stackexchange.com/questions/192701/how-to-remove-duplicate-files-using-bash

Все эти решения нуждаются начать с хеширования. Некоторые интересные идеи в некоторых из них: ха sh просто кусок обоих файлов, начинающийся где-то за заголовком, а затем проводить полное сравнение, только если они совпадают. Хорошая идея для двойной проверки работы, но предпочел бы делать это только на очень, очень немногих, которые на самом деле являются дубликатами. Поскольку, просматривая первые несколько тысяч из них вручную, ни один дубликат не был даже близко к другому файлу.

https://unix.stackexchange.com/questions/277697/whats-the-quickest-way-to-find-duplicated-files

Предложено:

$ find -not -empty -type f -printf "%s\n" | sort -rn | uniq -d | xargs -I{} -n1 find -type f -size {}c -print0 | xargs -0 md5sum | sort | uniq -w32 --all-repeated=separate

Перерывы для меня:

find: unknown option -- n
usage: find [-dHhLXx] [-f path] path ... [expression]
uniq: unknown option -- w
usage: uniq [-ci] [-d | -u] [-f fields] [-s chars] [input_file [output_file]]
find: unknown option -- t
usage: find [-dHhLXx] [-f path] path ... [expression]
xargs: md5sum: No such file or directory

https://unix.stackexchange.com/questions/170693/compare-directory-trees-regarding-file-name-and-size-and-date

Не удалось выяснить как rsync -nrvc --delete может работать в том же каталоге, но там может быть решение.

Ну, как насчет cmp? Да, это выглядит довольно хорошо, на самом деле!

cmp -z file1 file2

Облом, моя версия cmp не включает параметр размера -z.

Однако, Я попытался реализовать это просто ради ухмылки - и когда это не удалось, глядя на это, я понял, что мне также нужна помощь в построении моего l oop logi c. Удаление вещей из моих петель в процессе их обработки - это, вероятно, рецепт поломки, да.

if [ ! -d ../Dupes/ ]; then
mkdir ../Dupes/ || exit 1       # Cuz no set -e, and trap not working
fi
for i in ./*
do
  for j in ./*
  do
  if [[ "$i" != "$j" ]]; then       # Yes, it will be identical to itself
     if [[ $(cmp -s "$i" "$j") ]]; then
        echo "null"         # Cuz I can't use negative of the comparison?
     else
        mv -i "$i" ../Dupes/
     fi
  fi   
  done   
done  

https://unix.stackexchange.com/questions/367749/how-to-find-and-delete-duplicate-files-within-the-same-directory

Возможно, есть что-то, что я мог бы использовать , но я не слежу за тем, что там происходит.

https://superuser.com/questions/259148/bash-find-duplicate-files-mac-linux-compatible

Если бы это было что-то, что возвращает размер, а не md5, возможно, один из здесь ответы?

https://unix.stackexchange.com/questions/570305/what-is-the-most-efficient-way-to-find-duplicate-files

Не получили ответа.

TIL: При отправке ошибок из . scriptname мой терминал закроется мгновенно. Спасибо, Google!

TIL: отправка ошибок из скриптов, выполненных через $ PATH, закроет терминал, если в профиле установлены shopt -s extdebug + trap checkcommand DEBUG, чтобы попытаться поймать rm -r * - но, по крайней мере, будет уважать мой псевдоним для exit

TIL: Backticks устарели, используйте $ ( вещи ) - Тьфу, столько переписывания нужно сделать: P

TIL: Как поймать не-ascii символы в именах файлов без использования basename

TIL: "${file##*/}"

TIL: file - да, X.pdf не является PDF.

1 Ответ

3 голосов
/ 04 мая 2020

По поводу POSIX

Боюсь, вы не можете получить фактический размер файла (не количество блоков, выделенных файлом) в простой оболочке posix без использования ls. Все решения, такие как du --apparent-size, find -printf %s и stat, не являются posix.
Однако, пока ваши имена файлов не содержат переносов (пробелы в порядке), вы можете создавать безопасные решения, полагаясь на ls , Для корректной обработки имен файлов с помощью переносов строк в любом случае потребуются очень не-posix-инструменты (например, GNU sort -z).

Подход Bash + POSIX Фактическое сравнение файлов

Я бы отказался от подхода для сравнения только Размеры файлов и используйте cmp вместо. Для огромных каталогов сценарий posix будет медленным независимо от того, что вы делаете. Кроме того, я ожидаю, что cmp выполнит некоторые быстрые проверки (например, сравнение размеров файлов) перед тем, как сравнивать содержимое файла. Для обычного сценария ios с несколькими файлами скорость в любом случае не должна иметь значения, так как даже худший сценарий будет работать достаточно быстро.

Следующий сценарий размещает каждую группу реальных дубликатов (как минимум два, но может быть подробнее) в свой собственный подкаталог dups/. Скрипт должен работать со всеми именами файлов; пробелы, специальные символы и даже переносы строк в порядке. Обратите внимание, что мы все еще используем bash (это не posix). Мы просто предполагаем, что все инструменты (такие как mv, find, ...) имеют posix.

#! /usr/bin/env bash
files=()
for f in *; do [ -f "$f" ] && files+=("$f"); done
max=${#files[@]}
for (( i = 0; i < max; i++ )); do
    sameAsFileI=()
    for (( j = i + 1; j < max; j++ )); do
        cmp -s "${files[i]}" "${files[j]}" &&
        sameAsFileI+=("${files[j]}") &&
        unset 'files[j]'
    done
    (( ${#sameAsFileI[@]} == 0 )) && continue
    mkdir -p "dups/$i/"
    mv "${files[i]}" "${sameAsFileI[@]}" "dups/$i/"
    # no need to unset files[i] because loops won't visit this entry again
    files=("${files[@]}") # un-sparsify array
    max=${#files[@]}
done

Довольно переносимый подход без POSIX с использованием только размеров файлов

Если вы нужен более быстрый подход, который сравнивает только те размеры файлов, которые я предлагаю, чтобы не использовал вложенный l oop. Циклы в bash уже медленные, но если вы их вложите, у вас будет квадратичная c временная сложность. Это быстрее и проще ...

  1. печатать только файлы с размерами без имен файлов
  2. применять sort | uniq -d для получения дубликатов во времени O (n log n)
  3. Переместить все файлы, имеющие один из дублированных размеров, в каталог

Это решение не строго соответствует posix. Однако я попытался проверить, что инструменты и опции в этом решении поддерживаются большинством реализаций. Ваш find должен поддерживать не-posix опции -maxdepth и -printf с %s для фактического размера файла и %f для базового имени файла (%p для полного пути также будет приемлемым).

Следующий скрипт помещает все файлы одинакового размера в каталог potential-dups/. Если есть два файла размером n и два файла размером m, все четыре файла попадают в этот единственный каталог. Скрипт должен работать со всеми именами файлов, за исключением тех, которые имеют разрывы строк (то есть, \n; \r должно быть в порядке).

#! /usr/bin/env sh
all=$(find . -maxdepth 1 -type f -printf '%s %f\n' | sort)
dupRegex=$(printf %s\\n "$all" | cut -d' ' -f1 | uniq -d |
  sed -e 's/[][\.|$(){}?+*^]/\\&/g' -e 's/^/^/' | tr '\n' '|' | sed 's/|$//')
[ -z "$dupRegex" ] && exit
mkdir -p potential-dups
printf %s\\n "$all" | grep -E "$dupRegex" | cut -d' ' -f2- |
  sed 's/./\\&/' | xargs -I_ mv _ potential-dups

На случай, если вам интересно узнать о некоторых из команд sed: Они заключают в кавычки имена файлов так, чтобы последующие инструменты правильно обрабатывали пробелы и специальные символы. sed 's/[][\.|$(){}?+*^]/\\&/g' предназначен для преобразования необработанных строк в эквивалентные расширенные регулярные выражения (ERE) , а sed 's/./\\&/' - для обработки литералов xargs. См. posix документацию xargs :

-I replstr [...] Любые <blank> s в начале каждой строки должны игнорироваться.
[. ..]
Обратите внимание, что правила цитирования, используемые xargs, не совпадают с правилами оболочки. [...] Простое правило, которое можно использовать для преобразования любой строки в форму в кавычках, правильно интерпретируемую xargs, состоит в том, что перед каждым символом в строке должен стоять обратный символ sh.

...