LINUX.ORG.RU

Сравнить и объединить файлы с пометкой различий


0

1

Имеется несколько почти одинаковых текстовых файлов-списков. Требуется их сравнить и составить объединённый список, содержащий все пункты всех списков в том же порядке. Если пункт есть не во всех файлах, нужно добавить метку, в каких файлах он есть.

Пока обошёлся

diff -y -W 300 --left-column

с последующей обработкой sed-ом:

sed 's/^\(.*\)[ \t]*($/\1/'

sed 's/^\(.*\)[ \t]*<$/(file1) \1/'

sed 's/^[ \t]*>\t\(.*\)$/(file2) \1/'

sed 's/^\(.*\)[ \t]*|\t\(.*\)$/\1/'

и небольшой правкой руками. Повезло, что было очевидно, как распределятся различия.

Существуют ли более простые способы?

На всякий случай: здесь очень старая система, diff 2.8.7, датирован 2004 годом.

★★★★★

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

zolden ★★★★★
()
Ответ на: комментарий от zolden

Ладно.

Файл M1D.txt:

File
File > Open
File > Export U
File > Close
File > Exit

Файл U1D.txt:

File
File > Open
File > Export M
File > Close
File > Exit

Файл M2D.txt:

File
File > Open
File > Export 1D 
File > Close
File > Exit
Tools
Tools > Extract

Файл U2D.txt:

File
File > Open
File > Export 1D 
File > Close
File > Exit
Tools
Tools > Slice

Сводный файл:

File
File > Open
(M1D.txt) File > Export U
(U1D.txt) File > Export M
(M2D.txt, U2D.txt) File > Export 1D 
File > Close
File > Exit
(M2D.txt, U2D.txt) Tools
(M2D.txt) Tools > Extract
(U2D.txt) Tools > Slice
Если строка есть во всех файлах, она войдёт без изменений. Если не во всех — в начало строки добавится список файлов, где она есть.

Можно результат представить и как-то по-другому. Лишь бы он включал все строки всех файлов и пометки, где есть необщие строки.

Я специально не оговорил переставление строк. То, как его обрабатывает diff — приемлемо.

Что ещё было непонятного в первом посте?

question4 ★★★★★
() автор топика
Ответ на: комментарий от question4

Сортировать совсем нельзя?

anonymous
()
Ответ на: комментарий от question4

Судя по всему файлы описывают структуру меню и строки внутри каждого не могут дублироваться. В лоб я бы наверно сделал с помощью грепа и ассоциативных массивов шелла.
Делаем цикл по файлам + делаем вложенный цикл по строкам (current_line) каждого текущего файла и грепаем в остальных эту строчку. Результаты грепа по каждому файлу запоминаем. В результирующий файл выводим либо current_line если найдена везде, либо имена файлов с совпадениями + current_line. В конце удаляем дублирующиеся строки.
Чуть попозжу набросаю в более машинном виде.

zolden ★★★★★
()

«Старая система» это как я понимаю 2й баш? А есть awk? Ну и критерии простоты озвучили бы заодно

zolden ★★★★★
()
Ответ на: комментарий от question4

awk

awk '{a[$0]=a[$0]" "FILENAME;b[$0]++} END{for (i in a) {if (b[i] == "4"){a[i]=""} else{a[i]=a[i]":"};print a[i],i}}' *.txt


тут 4 - количество входных файлов, не знаю как без хардкода это сделать, может гуру подскажут.
Вывод на ваших данных:
File > Exit
M1D.txt: File > Export U
M2D.txt U2D.txt: Tools
M2D.txt U2D.txt: File > Export 1D
File > Open
U1D.txt: File > Export M
U2D.txt: Tools > Slice
File
M2D.txt: Tools > Extract
File > Close

zolden ★★★★★
()
Ответ на: комментарий от zolden

> Судя по всему файлы описывают структуру меню

Не только. Ещё и вызываемых диалогов.

и строки внутри каждого не могут дублироваться.

Могут :( Что-нибудь вроде

\t\t\t\t\tOK

В лоб я бы наверно сделал с помощью грепа и ассоциативных массивов шелла.

Делаем цикл по файлам + делаем вложенный цикл по строкам (current_line) каждого текущего файла и грепаем в остальных эту строчку. Результаты грепа по каждому файлу запоминаем. В результирующий файл выводим либо current_line если найдена везде, либо имена файлов с совпадениями + current_line. В конце удаляем дублирующиеся строки.

Спасибо за подсказки, вечером попробую.

«Старая система» это как я понимаю 2й баш? А есть awk?

Очень желательно, чтобы решение работало и под оффтопиком :) А это озвученная версия diff и bash 3.1.17 из MSYS.

Ну и критерии простоты озвучили бы заодно

Вообще-то ожидал, что это умеет diff с какими-то ключами. Или что-нибудь из diffutils/coreutils/moreutils.

В идеале, желаемый результат должна выдавать команда вида:

diff2one [options] file1 file2 [file3 ...]

:)

question4 ★★★★★
() автор топика
Ответ на: awk от zolden

без хардкода количества файлов

awk '{a[$0]=a[$0]" "FILENAME;b[$0]++} END{for (i in a) {if (b[i] == ARGC-1){a[i]=""} else{a[i]=a[i]":"};print a[i],i}}' *.txt

zolden ★★★★★
()
Ответ на: комментарий от zolden

Спасибо. Различия находит, но при этом сильно тасует строки. В таком виде подходит даже хуже, чем серия diff + sed.

Будет много времени — попробую в нём разобраться.

Кстати, gawk из MinGW сработал так же, как и линуксовый.

question4 ★★★★★
() автор топика
Ответ на: комментарий от question4

Тут используются ассоциативные массивы, как вы видите. Т.е. если первый файл содержит n уникальных строк, то у нас будет n массивов с индексами в виде содержимого этих строк. Если следующий файл длиннее на x строк, то будет уже n+x массивов. Да, вывод идёт отсортированный по индексу массива, поэтому вряд ли тут что-то можно улучшить-надо всё переделывать. Если у вас файлы содержат примерно одинаковые строки в одинаковом порядке это конечно ухудшает читабельность. Но такого исходного условия не было, так что деньги я вам не верну.

zolden ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.