РЕШЕНО - сравнить файлы (сравнить файлы по содержимому)
Модераторы: /dev/random, Модераторы разделов
-
- Сообщения: 162
- ОС: Devuan
РЕШЕНО - сравнить файлы
Опят прошу помощи форумчан, извините, что обращаюсь к вам за решением, так как сам решения не нашёл. Вернее решение нашёл, но оно для серьёзного практического применения не годиться.
Есть два каталога с подкаталогами, надо рекурсивно сравнить файлы по содержимому но не по названию. Найти рекурсивно, (путь где находится файл не важен и не важно его название, могут отличаться в названии и в местонахождении), (файлы обычные бинарники, текстовые, архивы и т.д. но не ссылки, пайпы, блочные устройства и т.д.), файлы которые различаются по содержимому и записать в массив только файлы которые находятся в одной из директорий, например в первой.
Перепробовал и diff и rsync но ничего из этого не подошло, даже воспользоваться информацией полученной от diff или rsync не получилось, так как выдают не то что нужно.
Решение нашёл прямое, из первой папки вычисляю все md5sum каждого файла и заношу в массив, файл и его md5 сумму, потом из второй папки делаю то же самое.
Потом в двойном цикле сравниваю эти массивы, то есть из первого беру первое значение и сравниваю со всеми значениями из второго массива, потом из первого беру второе значение и сравниваю опять со всеми значениями второго массива и т.д.
Потом на основании этого сравнения формирую окончательный массив где будет лежать перечень файлов из первой папки отличающийся по содержанию с файлами со второй при этом они могут иметь одинаковые имена.
Данный способ работает отлично, но для практического применения вряд ли годится, вернее годится когда файлов мало и они небольшого размера. Кода как в моём случае их очень много и есть большого размера, то на выполнение данного скрипта уходит не часы - дни.
А быстрое решение я так и не нашёл.
Помогите кто знает как сделать или уже сталкивался с таким.
Есть два каталога с подкаталогами, надо рекурсивно сравнить файлы по содержимому но не по названию. Найти рекурсивно, (путь где находится файл не важен и не важно его название, могут отличаться в названии и в местонахождении), (файлы обычные бинарники, текстовые, архивы и т.д. но не ссылки, пайпы, блочные устройства и т.д.), файлы которые различаются по содержимому и записать в массив только файлы которые находятся в одной из директорий, например в первой.
Перепробовал и diff и rsync но ничего из этого не подошло, даже воспользоваться информацией полученной от diff или rsync не получилось, так как выдают не то что нужно.
Решение нашёл прямое, из первой папки вычисляю все md5sum каждого файла и заношу в массив, файл и его md5 сумму, потом из второй папки делаю то же самое.
Потом в двойном цикле сравниваю эти массивы, то есть из первого беру первое значение и сравниваю со всеми значениями из второго массива, потом из первого беру второе значение и сравниваю опять со всеми значениями второго массива и т.д.
Потом на основании этого сравнения формирую окончательный массив где будет лежать перечень файлов из первой папки отличающийся по содержанию с файлами со второй при этом они могут иметь одинаковые имена.
Данный способ работает отлично, но для практического применения вряд ли годится, вернее годится когда файлов мало и они небольшого размера. Кода как в моём случае их очень много и есть большого размера, то на выполнение данного скрипта уходит не часы - дни.
А быстрое решение я так и не нашёл.
Помогите кто знает как сделать или уже сталкивался с таким.
Последний раз редактировалось v4567 24.04.2019 18:00, всего редактировалось 1 раз.
-
- Сообщения: 162
- ОС: Devuan
Re: сравнить файлы
Наверное как то можно решить при помощи rsync с опцией -n но другие опции и их комбинации подходящие под мою задачу так и не нашёл. Не получается сделать при помощи rsync. С diff дела ещё хуже.
-
- Администратор
- Сообщения: 5372
- ОС: Gentoo
Re: сравнить файлы
Есть специализированные утилиты для поиска дубликатов файлов, вроде fdupes и duff (обе должны быть в большинстве дистрибутивов). Они именно по хешам и сравнивают, но для ускорения работы сначала сравнивают размеры, а хеши вычисляют только для тех файлов, размеры которых не уникальны. Ну и, разумеется, готовые хеши сравнивают более умными способами, чем каждый с каждым.
В вашем случае, как я понимаю, раз вы ищете не дубли, а наоборот, уникальные файлы, большинство файлов у вас - дубли, верно? Если так, то эта оптимизация по уникальности размера особого прироста производительности не даст, а более быстрого способа нет. Но в любом случае, всё же рекомендую не изобретать велосипед, а воспользоваться этими готовыми утилитами.
В вашем случае, как я понимаю, раз вы ищете не дубли, а наоборот, уникальные файлы, большинство файлов у вас - дубли, верно? Если так, то эта оптимизация по уникальности размера особого прироста производительности не даст, а более быстрого способа нет. Но в любом случае, всё же рекомендую не изобретать велосипед, а воспользоваться этими готовыми утилитами.
-
- Сообщения: 162
- ОС: Devuan
-
- Сообщения: 162
- ОС: Devuan
Re: сравнить файлы
Не совсем подошёл fdupes и duff. Они ищут дубликаты в одной директории. Если я в одну папку сложу две своих директории, то может получиться что у меня в первой директории будут два одинаковых по содержанию файла но они будут отличаться от файлов второй директории. в результате будет ошибка.
Нашёл вот такую конструкцию, этой конструкцией я нахожу дубликаты, а потом убираю их из первой директории и получаю файлы отличные по содержанию от файлов второй директории.
Но что сбило с ног, вот вывод массива prommasdir1:
Получается всё записалось в одну ячейку массива, да ещё и с пустой строкой. Никак не могу это побороть, а из-за этого не работает всё дальше.
Нашёл вот такую конструкцию, этой конструкцией я нахожу дубликаты, а потом убираю их из первой директории и получаю файлы отличные по содержанию от файлов второй директории.
Код: Выделить всё
readarray -d '' prommasdir1 < <(sha1sum "$dir1"* "$dir2"* 2> /dev/null | sort -k1 | uniq -w 40 --all-repeated=none | cut -d\ -f3- | grep "$1" | sort)
Код: Выделить всё
printf "%s\n" "${prommasdir1[0]}"
Код: Выделить всё
/home/user/888888/dir1/11
/home/user/888888/dir1/55
Добавлено (23:38):
толком так и не могу понять, что означает эта конструкция:Код: Выделить всё
< <(......)
-
- Администратор
- Сообщения: 5372
- ОС: Gentoo
Re: сравнить файлы
Вы выводите файлы, разделяя их переводом строки, а читаете так, будто они разделены символом \0.
<(...) - создать именованный канал (или анонимный и использовать "фальшивое" имя через /proc или /dev/fd), запустить команду "..." в фоне с перенаправлением вывода в этот канал и подставить имя канала вместо этой конструкции.
< ... - читать из того, что подставлено вместо "...". В данном случае - того канала.
Shell
$ ls -l <(sleep 10)
lr-x------ 1 user user 64 апр 22 00:59 /dev/fd/63 -> pipe:[9341348]
-
- Сообщения: 162
- ОС: Devuan
Re: сравнить файлы
Правильно ли я понял, надо в конструкцию:/dev/random писал: ↑22.04.2019 00:02Вы выводите файлы, разделяя их переводом строки, а читаете так, будто они разделены символом \0.
Код: Выделить всё
readarray -d '' prommasdir1 < <(sha1sum "$dir1"* "$dir2"* 2> /dev/null | sort -k1 | uniq -w 40 --all-repeated=none | cut -d\ -f3- | grep "$1" | sort)
Добавлено (00:20):
Код: Выделить всё
echo -e "1111\n2222" > 1122
cat 1122 | grep "1111"
1111
(cat 1122)> > grep "1111"
bash: синтаксическая ошибка рядом с неожиданным маркером «>»
grep "1111" < <(cat 1122)
1111
-
- Администратор
- Сообщения: 5372
- ОС: Gentoo
Re: сравнить файлы
Нет. У вас там и так переводов строки полно. Вот это:
Shell
sha1sum "$dir1"* "$dir2"* 2> /dev/null | sort -k1 | uniq -w 40 --all-repeated=none | cut -d\ -f3- | grep "$1" | sort
-
- Сообщения: 162
- ОС: Devuan
Re: сравнить файлы
sha1sum не может поставить разделитель \0 поэтому убираю -d ''
Теперь если не ошибаюсь мне для массива, что бы не было пустых строк, надо поменять \n на \0. При помощи sed не получилось, просто так лихо поменять, поэтому просто удаляю \n:
Но prom1masdir1 пустой, да ещё и с одним переносом строки \n
выдаёт:
Я уже просто измучился.
Теперь если не ошибаюсь мне для массива, что бы не было пустых строк, надо поменять \n на \0. При помощи sed не получилось, просто так лихо поменять, поэтому просто удаляю \n:
Код: Выделить всё
readarray d '' prom1masdir1 < <(printf "%s\0" "${prommasdir1[@]}" | sed ':a;N;$!ba;s/\n//g')
Код: Выделить всё
printf "%s" "${prom1masdir1[@]}"
Код: Выделить всё
-
- Сообщения: 904
- ОС: debian, fedora (i3-wm)
Re: сравнить файлы
Код: Выделить всё
uniqfiles=($(find one/dir two/dir -type f -not -name '.*' | grep -vxf <(fdupes -rf one/dir two/dir)))
echo ${uniqfiles[@]}
-
- Сообщения: 3728
- Статус: Многоуважаемый джинн...
- ОС: Slackware64-14.1/14.2
Re: сравнить файлы
Это всегда должно работать?
Я из любопытства попробовал, получил ошибку:
Shell
sh: syntax error near unexpected token `('
Пробовал два разных эмулятора терминала.
Пробовал виртуальную консоль (которая Alt+Ctrl+F1).
Везде одно и то же: ошибка.
Непонятно, от чего это зависит.
-
- Модератор
- Сообщения: 21046
- Статус: nulla salus bello
- ОС: Debian GNU/Linux
Re: сравнить файлы
Всегда, но не везде. Это башизм.
Пишите правильно:
в консоли вку́пе (с чем-либо) в общем вообще | в течение (часа) новичок нюанс по умолчанию | приемлемо проблема пробовать трафик |
-
- Администратор
- Сообщения: 5372
- ОС: Gentoo
Re: сравнить файлы
Это делать было не обязательно, вы могли запустить по экземпляру sha1sum на файл и вывести \0 между их результатами. Но убрали - так убрали.
Зачем?
Не видя конкретной исправленной команды, не могу сказать, откуда у вас берутся пустые строки, но замена \n на \0 тут точно ни при чём.
Во-первых, эта команда вообще не нужна. Во-вторых, вы пропустили "-" перед "d".
Замечательная идея, но реализация подкачала. Ваш вариант споткнётся на любых необычных символах в именах файлов, даже пробелах.olecya писала: ↑22.04.2019 05:03Код: Выделить всё
uniqfiles=($(find one/dir two/dir -type f -not -name '.*' | grep -vxf <(fdupes -rf one/dir two/dir)))
Спасибо сказали:
-
- Сообщения: 162
- ОС: Devuan
Re: сравнить файлы
Всем огромное спасибо за помощь!
olecya, спасибо за вашу конструкцию, но она немного не правильно работает. Выдаёт все файлы в двух директориях. Не стал уже разбираться почему.
cmp - не пробовал.
Пришёл к вот такой конструкции. Проверил - корректно работает с файлами в именах которых пробелы и символы перевода строк.
В массивы решил не писать, так как потом надо из них читать в файл, а это лишние секунды (секунды когда файлов очень много), а когда таких разных конструкций выборок очень много, то секунды к секундам и уже затрачивается лишнее время. Поэтому решил писать сразу в файл отчёта.
Кстати у меня:
работает. Эмулятор терминала konsole
Версия 16.12.0
Программа использует:
KDE Frameworks 5.28.0,
Qt 5.7.1 (собрана с версией 5.7.1),
Оконную систему xcb.
Но на самом деле у меня devuan это ветка от debian без systemd.
Возник вот ещё какой вопрос.
Вот такая конструкция работает:
А вот такая уже нет:
И заменить символ перевода строки на символ конца строки при помощи sed не получилось:
Сразу извиняюсь за тупые вопросы. По sed документацию прочту, попозже, сейчас нет времени.
olecya, спасибо за вашу конструкцию, но она немного не правильно работает. Выдаёт все файлы в двух директориях. Не стал уже разбираться почему.
cmp - не пробовал.
Пришёл к вот такой конструкции. Проверил - корректно работает с файлами в именах которых пробелы и символы перевода строк.
Код: Выделить всё
promper1=$1
promper2=$((${#promper1}-1))
possim=$(echo ${promper1:$promper2:1})
if [[ "$possim" == "/" ]]
then
dir1="$1"
else
dir1="$1/"
fi
promper1=$2
promper2=$((${#promper1}-1))
possim=$(echo ${promper1:$promper2:1})
if [[ "$possim" == "/" ]]
then
dir2="$2"
else
dir2="$2/"
fi
(find "$1" -type f ; sha1sum "$dir1"* "$dir2"* 2> /dev/null | \
sort -k1 | uniq -w 40 --all-repeated=none | cut -d\ -f3- | grep "$1" | \
sort) | sort | uniq -u >> files.txt
Кстати у меня:
Код: Выделить всё
ls -l <(sleep 10)
Версия 16.12.0
Программа использует:
KDE Frameworks 5.28.0,
Qt 5.7.1 (собрана с версией 5.7.1),
Оконную систему xcb.
Код: Выделить всё
uname -a
Linux 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64 GNU/Linux
cat /etc/issue.net
Devuan GNU/Linux ascii
ls -l <(sleep 10)
lr-x------ 1 root root 64 апр 22 17:41 /dev/fd/63 -> pipe:[95389]
Возник вот ещё какой вопрос.
Вот такая конструкция работает:
Код: Выделить всё
readarray mas < <(.......)
Код: Выделить всё
........ | readarray mas
Код: Выделить всё
sed ':a;N;$!ba;s/\n/\0/g'
-
- Модератор
- Сообщения: 21046
- Статус: nulla salus bello
- ОС: Debian GNU/Linux
Re: сравнить файлы
Вы так и не ответили, зачем Вам это.
Пишите правильно:
в консоли вку́пе (с чем-либо) в общем вообще | в течение (часа) новичок нюанс по умолчанию | приемлемо проблема пробовать трафик |
-
- Сообщения: 904
- ОС: debian, fedora (i3-wm)
Re: сравнить файлы
Не очень понимаю что вы делаете, но может это поможет)))
Код: Выделить всё
sed -z 's/\n/\x0/g'
-
- Сообщения: 162
- ОС: Devuan
Re: сравнить файлы
olecya
Пришлось отказаться от подсчёта контрольных сумм, так как могут попасться файлы устройств. Подсчитать контрольную сумму которых невозможно, так что от sha1sum пришлось отказаться. Вернулся к fdupes и сделал вот такую конструкцию. Кстати fdupes не считает контрольные суммы файлов устройств.
Эта конструкция выдаст список файлов не совпадающих по содержимому которые находятся во второй директории.
Пришлось отказаться от подсчёта контрольных сумм, так как могут попасться файлы устройств. Подсчитать контрольную сумму которых невозможно, так что от sha1sum пришлось отказаться. Вернулся к fdupes и сделал вот такую конструкцию. Кстати fdupes не считает контрольные суммы файлов устройств.
Код: Выделить всё
(find "dir2" -type f ; fdupes -r "dir1" "dir2" | grep -v "/dir1") | sort | uniq -u >> file