РЕШЕНО - сравнить файлы (сравнить файлы по содержимому)

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

Ответить
v4567
Сообщения: 162
ОС: Devuan

РЕШЕНО - сравнить файлы

Сообщение v4567 »

Опят прошу помощи форумчан, извините, что обращаюсь к вам за решением, так как сам решения не нашёл. Вернее решение нашёл, но оно для серьёзного практического применения не годиться.
Есть два каталога с подкаталогами, надо рекурсивно сравнить файлы по содержимому но не по названию. Найти рекурсивно, (путь где находится файл не важен и не важно его название, могут отличаться в названии и в местонахождении), (файлы обычные бинарники, текстовые, архивы и т.д. но не ссылки, пайпы, блочные устройства и т.д.), файлы которые различаются по содержимому и записать в массив только файлы которые находятся в одной из директорий, например в первой.
Перепробовал и diff и rsync но ничего из этого не подошло, даже воспользоваться информацией полученной от diff или rsync не получилось, так как выдают не то что нужно.
Решение нашёл прямое, из первой папки вычисляю все md5sum каждого файла и заношу в массив, файл и его md5 сумму, потом из второй папки делаю то же самое.
Потом в двойном цикле сравниваю эти массивы, то есть из первого беру первое значение и сравниваю со всеми значениями из второго массива, потом из первого беру второе значение и сравниваю опять со всеми значениями второго массива и т.д.
Потом на основании этого сравнения формирую окончательный массив где будет лежать перечень файлов из первой папки отличающийся по содержанию с файлами со второй при этом они могут иметь одинаковые имена.
Данный способ работает отлично, но для практического применения вряд ли годится, вернее годится когда файлов мало и они небольшого размера. Кода как в моём случае их очень много и есть большого размера, то на выполнение данного скрипта уходит не часы - дни.
А быстрое решение я так и не нашёл.
Помогите кто знает как сделать или уже сталкивался с таким.
Последний раз редактировалось v4567 24.04.2019 18:00, всего редактировалось 1 раз.
Спасибо сказали:
v4567
Сообщения: 162
ОС: Devuan

Re: сравнить файлы

Сообщение v4567 »

Наверное как то можно решить при помощи rsync с опцией -n но другие опции и их комбинации подходящие под мою задачу так и не нашёл. Не получается сделать при помощи rsync. С diff дела ещё хуже.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: сравнить файлы

Сообщение /dev/random »

Есть специализированные утилиты для поиска дубликатов файлов, вроде fdupes и duff (обе должны быть в большинстве дистрибутивов). Они именно по хешам и сравнивают, но для ускорения работы сначала сравнивают размеры, а хеши вычисляют только для тех файлов, размеры которых не уникальны. Ну и, разумеется, готовые хеши сравнивают более умными способами, чем каждый с каждым.

В вашем случае, как я понимаю, раз вы ищете не дубли, а наоборот, уникальные файлы, большинство файлов у вас - дубли, верно? Если так, то эта оптимизация по уникальности размера особого прироста производительности не даст, а более быстрого способа нет. Но в любом случае, всё же рекомендую не изобретать велосипед, а воспользоваться этими готовыми утилитами.
Спасибо сказали:
v4567
Сообщения: 162
ОС: Devuan

Re: сравнить файлы

Сообщение v4567 »

/dev/random Огромное спасибо!
Пытаюсь сделать при помощи duff.
Тогда отпишусь о результатах.
Спасибо сказали:
v4567
Сообщения: 162
ОС: Devuan

Re: сравнить файлы

Сообщение v4567 »

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

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

Код: Выделить всё

readarray -d '' prommasdir1 < <(sha1sum "$dir1"* "$dir2"* 2> /dev/null | sort -k1 | uniq -w  40 --all-repeated=none | cut -d\  -f3- | grep "$1" | sort)
Но что сбило с ног, вот вывод массива prommasdir1:

Код: Выделить всё

printf "%s\n" "${prommasdir1[0]}"

Код: Выделить всё

/home/user/888888/dir1/11
/home/user/888888/dir1/55

Получается всё записалось в одну ячейку массива, да ещё и с пустой строкой. Никак не могу это побороть, а из-за этого не работает всё дальше.
Добавлено (23:38):
толком так и не могу понять, что означает эта конструкция:

Код: Выделить всё

< <(......)
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: сравнить файлы

Сообщение /dev/random »

v4567 писал:
21.04.2019 23:36
Получается всё записалось в одну ячейку массива, да ещё и с пустой строкой.
Вы выводите файлы, разделяя их переводом строки, а читаете так, будто они разделены символом \0.
v4567 писал:
21.04.2019 23:36
толком так и не могу понять, что означает эта конструкция:
<(...) - создать именованный канал (или анонимный и использовать "фальшивое" имя через /proc или /dev/fd), запустить команду "..." в фоне с перенаправлением вывода в этот канал и подставить имя канала вместо этой конструкции.
< ... - читать из того, что подставлено вместо "...". В данном случае - того канала.

Shell

$ ls -l <(sleep 10)
lr-x------ 1 user user 64 апр 22 00:59 /dev/fd/63 -> pipe:[9341348]
Спасибо сказали:
v4567
Сообщения: 162
ОС: Devuan

Re: сравнить файлы

Сообщение v4567 »

/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)
вставить перевод строки - \n. Но как?
Добавлено (00:20):

Код: Выделить всё

echo -e "1111\n2222" > 1122
cat 1122 | grep "1111"
1111
(cat 1122)> > grep "1111"
bash: синтаксическая ошибка рядом с неожиданным маркером «>»
grep "1111" < <(cat 1122)
1111
< <(....) типа пайп с левой стороны...
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: сравнить файлы

Сообщение /dev/random »

v4567 писал:
22.04.2019 00:17
Правильно ли я понял, надо в конструкцию:
...
вставить перевод строки - \n. Но как?
Нет. У вас там и так переводов строки полно. Вот это:

Shell

sha1sum "$dir1"* "$dir2"* 2> /dev/null | sort -k1 | uniq -w 40 --all-repeated=none | cut -d\ -f3- | grep "$1" | sort
выводит список файлов, разделённых переводом строки. А вот это: readarray -d '' prommasdir1 читает фрагменты ввода, разделённые \0, и записывает в массив. Т.к. у вас нет этих \0, всё содержимое становится одним фрагментом. Если вас устраивает невозможность обрабатывать файлы с переводами строки в именах, то уберите -d '', чтобы считать разделителями переводы строки. Если не устраивает - переделывайте команду в скобках, чтобы она использовала \0 вместо переводов строки.
Спасибо сказали:
v4567
Сообщения: 162
ОС: Devuan

Re: сравнить файлы

Сообщение v4567 »

sha1sum не может поставить разделитель \0 поэтому убираю -d ''

Теперь если не ошибаюсь мне для массива, что бы не было пустых строк, надо поменять \n на \0. При помощи sed не получилось, просто так лихо поменять, поэтому просто удаляю \n:

Код: Выделить всё

readarray d '' prom1masdir1 < <(printf "%s\0" "${prommasdir1[@]}" | sed ':a;N;$!ba;s/\n//g')
Но prom1masdir1 пустой, да ещё и с одним переносом строки \n

Код: Выделить всё

printf "%s" "${prom1masdir1[@]}"
выдаёт: Я уже просто измучился.
Спасибо сказали:
Аватара пользователя
olecya
Сообщения: 900
ОС: debian, fedora (i3-wm)

Re: сравнить файлы

Сообщение olecya »

Код: Выделить всё

uniqfiles=($(find one/dir two/dir -type f -not -name '.*' | grep -vxf <(fdupes -rf one/dir two/dir)))
echo ${uniqfiles[@]}
Спасибо сказали:
Аватара пользователя
ormorph
Сообщения: 2600
ОС: Gentoo

Re: сравнить файлы

Сообщение ormorph »

cmp
Спасибо сказали:
Аватара пользователя
Hephaestus
Сообщения: 3729
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2
Контактная информация:

Re: сравнить файлы

Сообщение Hephaestus »

/dev/random писал:
22.04.2019 00:02
ls -l <(sleep 10)
Это всегда должно работать?
Я из любопытства попробовал, получил ошибку:

Shell

sh: syntax error near unexpected token `('
Пробовал ставить разные значения $TERM.
Пробовал два разных эмулятора терминала.
Пробовал виртуальную консоль (которая Alt+Ctrl+F1).

Везде одно и то же: ошибка.
Непонятно, от чего это зависит.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: сравнить файлы

Сообщение Bizdelnick »

Hephaestus писал:
22.04.2019 09:50
Это всегда должно работать?
Всегда, но не везде. Это башизм.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5282
ОС: Gentoo

Re: сравнить файлы

Сообщение /dev/random »

v4567 писал:
22.04.2019 01:39
sha1sum не может поставить разделитель \0 поэтому убираю -d ''
Это делать было не обязательно, вы могли запустить по экземпляру sha1sum на файл и вывести \0 между их результатами. Но убрали - так убрали.
v4567 писал:
22.04.2019 01:39
Теперь если не ошибаюсь мне для массива, что бы не было пустых строк, надо поменять \n на \0.
Зачем?
Не видя конкретной исправленной команды, не могу сказать, откуда у вас берутся пустые строки, но замена \n на \0 тут точно ни при чём.
v4567 писал:
22.04.2019 01:39
Но prom1masdir1 пустой, да ещё и с одним переносом строки \n
Во-первых, эта команда вообще не нужна. Во-вторых, вы пропустили "-" перед "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)))
Замечательная идея, но реализация подкачала. Ваш вариант споткнётся на любых необычных символах в именах файлов, даже пробелах.
Спасибо сказали:
v4567
Сообщения: 162
ОС: Devuan

Re: сравнить файлы

Сообщение v4567 »

Всем огромное спасибо за помощь!

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)
работает. Эмулятор терминала konsole
Версия 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]
Но на самом деле у меня devuan это ветка от debian без systemd.


Возник вот ещё какой вопрос.
Вот такая конструкция работает:

Код: Выделить всё

readarray mas < <(.......)
А вот такая уже нет:

Код: Выделить всё

........   | readarray mas
И заменить символ перевода строки на символ конца строки при помощи sed не получилось:

Код: Выделить всё

sed ':a;N;$!ba;s/\n/\0/g'
Сразу извиняюсь за тупые вопросы. По sed документацию прочту, попозже, сейчас нет времени.
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: сравнить файлы

Сообщение Bizdelnick »

v4567 писал:
22.04.2019 17:55
заменить символ перевода строки на символ конца строки при помощи sed не получилось
Вы так и не ответили, зачем Вам это.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
olecya
Сообщения: 900
ОС: debian, fedora (i3-wm)

Re: сравнить файлы

Сообщение olecya »

Не очень понимаю что вы делаете, но может это поможет)))

Код: Выделить всё

sed -z 's/\n/\x0/g'
Спасибо сказали:
v4567
Сообщения: 162
ОС: Devuan

Re: сравнить файлы

Сообщение v4567 »

olecya
Пришлось отказаться от подсчёта контрольных сумм, так как могут попасться файлы устройств. Подсчитать контрольную сумму которых невозможно, так что от sha1sum пришлось отказаться. Вернулся к fdupes и сделал вот такую конструкцию. Кстати fdupes не считает контрольные суммы файлов устройств.

Код: Выделить всё

(find "dir2" -type f ; fdupes -r "dir1" "dir2" | grep -v "/dir1") | sort | uniq -u >> file
Эта конструкция выдаст список файлов не совпадающих по содержимому которые находятся во второй директории.
Спасибо сказали:
Ответить