Поиск дубликатов файлов с одинаковым именем и одинаковым размером

Нет не -хакерских способов перенаправить вывод другого процесса в другое место [1], но если a)вы не планируете когда-либо возвращать свой процесс на передний план и b)в порядке с другим процессом (легким -весомымcat)также работающим в течение всего времени работы вашей программы, это может привести к:

{ your_program... & } | (trap : INT; cat & sleep 5; kill $!; cat >/dev/null &)

Вы можете захотеть перенаправить как stderr, так и stdout вашей программы в конвейер с помощью your_program 2>&1.

[1] Возможный хакерский способ — подключиться к нему с помощью отладчика и перенаправить его вывод изнутри.

6
21.11.2021, 03:22
1 ответ

Вам действительно не нужны для этого внешние утилиты. Вы можете продвинуться довольно далеко, объединив набор стандартных команд, которые вы всегда можете запомнить, сохранив их в сценарии или функции оболочки.

Чтобы обнаружить дубликаты, вы можете хранить все пути к файлам в ассоциативном массиве с указанием размера и имени файла. Но поскольку вы указываете, что у вас большая коллекция файлов, я предлагаю вместо этого использовать сортировку. Затем отсортируйте второй раз с опцией уникальный , чтобы сравнение обоих результатов дало вам дубликаты.

  1. Вы хотите использовать результат первого сортирования дважды. Я могу использовать для этого временный файл; именованные каналы, fifo и тому подобное не требуют меньше усилий для этой простой работы. Команда mktempнапечатает для вас безопасное уникальное имя файла в каталоге /tmp/. С помощью команды , заменяющей конструкцией $(...), вы можете присвоить это имя переменной с именем tmpили любым другим. Повторите это, чтобы знать, что вы делаете.

  2. Теперь вы хотите использовать 2 ключевых поля :размер и имя файла, а также вам нужен полный путь. Для удобства косая черта /не является допустимым символом имени файла :, она зарезервирована для разделения имен каталогов и имен файлов. Как следствие, вы можете использовать /в качестве разделителя между этими 3 полями. Действие -printfкоманды findможет дать вам :найти все-type f(файлы )и распечатать их размер %s, имя файла %fи полный путь %p, все разделенные по /. Полные пути будут содержать больше косых черт,но мы знаем, что только первые 2 разделителя косой черты, которые мы явно указываем в формате -printf, являются нашими.

  3. Направить |вывод команды find, список размеров, имен файлов и полных путей, в команду sort. Скажите ему, что поля разделены косой чертой:-t /и что ключ находится в первых двух полях:-k 1,2. Вы можете сохранить отсортированный список сразу во временном файле, но я решил позволить сделать это команде tee, потому что таким образом вы можете оставить открытым один конвейер для повышения производительности.

  4. Направить |отсортированный список в tee, который сохраняет его копию в файле с заданным именем "$tmp"и как бы повторяет канал до следующего канала.

  5. Направить эхо|teeотсортированного списка второму sortпо тем же ключам, но теперь сделать вывод -uили --uniqueв ключах сортировки.

  6. Передать |список уникальных записей размера+имени файла команде diffв качестве первого ввода-(stdin)и использовать временный файл "$tmp"в качестве второго. Обычно diffдобавляет некоторую разметку к своему выводу, чтобы вы могли определить природу изменений. Но нам это не нужно, и это загромождает вывод для наших нужд. Мы знаем, что каждая запись уникального списка также находится в полном списке. Нам просто нужны простые дополнительные строки, которые есть только в полном списке. Это то, о чем просят опции команды diff.

  7. Приведенная выше команда выведет, надеюсь, краткий список файлов, у которых есть дубликаты. Один из каждого набора дубликатов опущен, потому что он все еще фигурирует в уникальном списке. Чтобы обработать каждую запись этого списка и воздействовать на его последнюю часть, полное имя пути, мы передаем |его в конструкцию while read. Это сохраняет каждую строку под заданным именем параметра dupl. В синтаксисе расширения параметра bash #мы опускаем префикс */*/, то есть размер плюс имя файла с разделителями косой черты /,и остается полный путь к дубликату файла. Я предлагаю вам сначала выполнить полную команду с echoи заменить ее командой удаления rmпосле тщательной проверки, или, что еще лучше, использовать одну из утилит корзины для перемещения дубликатов к вам или Trash, поэтому что ваш каталог очищен, но ничего не потеряно.

    tmp=$(mktemp)
    echo temp file is "$tmp"
    find -type f -printf "%s/%f/%p\n" |
     sort -t / -k 1,2 |
     tee "$tmp" |
     sort -t / -k 1,2 -u |
     diff --new-line-format="%L" --unchanged-line-format="" - "$tmp" |
     while read dupl;do echo "${dupl#*/*/}"; done
    rm "$tmp"
    
  8. Приберитесь за собой и очистите временный файл "$tmp".

2
21.11.2021, 12:23

Теги

Похожие вопросы