LINUX.ORG.RU

diff больше 3 файлов

 


1

1

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

Как это сделать проще и красивее, чем последовательно применяя diff -D ?

★★★

Грепом будет сложновато. Если файлов очень много, возможно будет быстрее и проще на питоне наскриптовать. Там, например, операция получения разности множеств есть.

TuxR ★★★★
()

Как это сделать проще и красивее, чем последовательно применяя diff -D ?

Не вижу прблемы, сверни их просто:
foldl diff -D result files

anonymous
()

Я бы на сях написал (самому как-то нужен был хитрый diff, написал, а потом на ЛОРе сказали, что есть уже что-то готовое).

Anon
()
Ответ на: комментарий от kostik87

Может вам просто про patch почитать ?

Взять или сделать файл с минимальным набором общих строк, сделать относительно него патчи для всех остальных файлов, пропатчить файл всеми патчами? Спасибо за идею.

Экспериментально выяснил: если есть 3 файла:

$ cat 1.txt
АААААААААА
ББББББББББ
ВВВВВВВВВВ

$ cat 2.txt
АААААААААА
ГГГГГГГГГГ
ВВВВВВВВВВ

$ cat 3.txt
АААААААААА
ВВВВВВВВВВ
Последовательность команд:
$ diff -c 3.txt 1.txt > 3-1
$ diff -c 3.txt 2.txt > 3-2
$ patch 4.txt 3-2
$ patch 4.txt 3-1
даст следующий результат:
$ cat 4.txt
АААААААААА
ББББББББББ
ГГГГГГГГГГ
ВВВВВВВВВВ

Если строк несколько, работает так же.

А как-нибудь можно помечать, какая строка откуда взялась?

И что это за команда merge, на которую ссылается man patch?

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

я бы по применял патчи и потом диффнул.

Спасибо, выше уже посоветовали. Результат приемлемый, но нельзя ли как-то ещё и ставить источник каждой необщей строки?

olegd ★★★
() автор топика

Кстати, можно просто сделать cat всех файлов, потом sort -u, а потом comm'ом пробежаться и сравнить каждый файл с кучей, помещая результат в выходной файл.

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

Я бы на сях написал (самому как-то нужен был хитрый diff, написал, а потом на ЛОРе сказали, что есть уже что-то готовое).

А я слышал про людей, которые тратили недели на создание тормозных аналогов grep и sed :) Поэтому и спрашиваю.

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

Ну, мой велосипед работал лишь в пару раз медленнее. Зато свое и легкое (а там был какой-то мегамонстр).

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

Кстати, можно просто сделать cat всех файлов, потом sort -u, а потом comm'ом пробежаться и сравнить каждый файл с кучей, помещая результат в выходной файл.

Забыл сказать: порядок строк критичен. Любое решение с sort не годится.

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

Надо пройти по списку и саккумулировать результат. Как это ещё сделать?

Сам свою команду пробовал? :)

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

Просто если всего-то построчно, то делается элементарно (если файлы небольшие): mmap'им все файлы, а потом построчно сравниваем при помощи strcmp. На каждую строчку при N файлах будет N*(N-1)/2 сравнений. Быстрее вряд ли получится.

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

Так их построчно надо сравнивать или все-таки одинаковые строки надо по всему файлу искать?

Сложно ответить. Сравниваемые файлы — длинные, почти одинаковые списки. В любой из списков может вклиниваться что-то своё. Конечный результат должен иметь все те же элементы в той же последовательности, что и в исходных. В каком порядке будут элементы, которые есть не везде — не принципиально.

Решение с patch даёт нужный результат, но не уточняет, откуда что взялось.

Пример того, что мне хотелось бы (на примере списка команд меню):

1-й список:
File
Open
Save
Save As
Close
Close All

2-й список:
File
Open
Import
Save As
Close

Результат:
File
Open
1:Import
Save
2:Save As
Close
1:Close All

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

смотрите ключ '-c'.

Я им и пользовался, но это не совсем то. Мне нужно, чтобы в конечном файле, получаемом применением patch, писалось, откуда взялась вставляемая строка. diff -D это обеспечивает, но если суммируются более 2 файлов, diff начинает сравнивать и #include, и запись получается громоздкой и путаной.

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

В смысле я применял diff -c, а patch опознавал формат и вёл себя так, как если бы я явно указал -c.

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

Тестируй:

/*
#include <sys/types.h>
#include <err.h>
*/
#include <stdio.h> // printf
#include <sys/mman.h> // mmap
#include <stdlib.h> // calloc, exit
#include <sys/stat.h> // open
#include <fcntl.h> // open
#include <unistd.h> // lseek
#include <string.h> // strchr

#ifdef DBG
	#define P(...) do{fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");}while(0)
#else
	#define P(...)
#endif

void help(char* name){
	printf("\nUsage:\n\t%s files\n\t\tprint lines of any file, that not present in other.\n", name);
	exit(1);
}

int main(int argc, char** argv){
	char **files, **curpos, **lines;
	off_t *sizes;
	int N = argc - 1, i;
	if(argc == 1) help(argv[0]);
	files = calloc(N, sizeof(char*));
	curpos = calloc(N, sizeof(char*));
	lines = calloc(N, sizeof(char*));
	sizes = calloc(N, sizeof(off_t));
	for(i = 0; i < N; i++){
		int f = open(argv[i+1], O_RDONLY);
		char *fname = argv[i+1];
		if(f < 0){
			fprintf(stderr, "Error opening file %s!\n", fname);
			exit(2);
		}
		off_t sz = lseek(f, 0, SEEK_END);
		sizes[i] = sz;
		P("size of %s: %zd", fname, sz);
		char *p = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, f, 0);
		if(!p){
			fprintf(stderr, "Can't mmap file %s!\n", fname);
			exit(3);
		}
		close(f);
		P("Content:\n%s", p);
		files[i] = curpos[i] = p;
	}
	int flag;
	do{
		flag = 0;
		for(i = 0; i < N; i++){
			char *L = strchr(curpos[i], '\n');
			if(!L){
				lines[i] = NULL;
				continue;
			}
			flag++;
			size_t len = L - curpos[i];
			lines[i] = strndup(curpos[i], len);
			curpos[i] += len+1;
		}
		if(!flag) break;
		for(i = N-1; i > -1; i--){
			int found = 0, j;
			if(!lines[i]) continue;
			for(j = N-1; j > -1; j--){
				if(i == j || !lines[j]) continue;
				P(">> cmp %s and %s", lines[i], lines[j]);
				if(strcmp(lines[i], lines[j]) == 0){
					found = 1;
					break;
				}
				if(found) continue;
			}
			if(!found)
				printf("#%s\n%s\n", argv[i+1], lines[i]);
		}
		for(i = 0; i < N; i++)
			free(lines[i]);
		P("flag: %d", flag);
	}while(flag > 1);
	for(i = 0; i < N; i++)
		munmap(files[i], sizes[i]);
	return 0;
}

Вот пример:

gcc -Wall -Werror mydiff.c  -o mydiff1 && ./mydiff1 00/* 
#00/6
а здесь уникальная строка
#00/3
строчка три
#00/6
и здесь
#00/5
строчка шесть
#00/3
строчка четыре
#00/2
строчка три
#00/1
строчка два
#00/6
строчка шесть
#00/5
строчки пять нет
#00/1
строчка три
#00/6
строчка два

Файлы:

grep "" 00/*
00/1:строчка раз
00/1:строчка два
00/1:строчка три
00/2:строчка раз
00/2:строчка три
00/2:а вот фигвам
00/3:строчка три
00/3:строчка четыре
00/3:а вот фигвам
00/3:нифига!
00/5:строчка раз
00/5:строчка шесть
00/5:строчки пять нет
00/5:нифига!
00/6:а здесь уникальная строка
00/6:и здесь
00/6:строчка шесть
00/6:строчка два
Anon
()
Ответ на: комментарий от olegd

В каком порядке будут элементы, которые есть не везде — не принципиально.

shit! раньше надо было говорить. В общем, патчь мой велосипед

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

Спасибо за ударный труд, но я сходу даже не представляю как оно работает, и с какой стороны к нему подступиться. Позже, когда высплюсь.

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

Тупо перебирает построчно все файлы из списка и сравнивает. Но порядок важен.

Anon
()

«Порядок важен»

В одном файле строки 1,2,3, а в другом — 3,2,1. Какой должен быть порядок строк в результате?

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

Такая ситуация не встретится. Пусть, для определённости, сравнивает так же, как diff, то есть общей строкой считается только 3. То есть результат будет 1, 2, 3, 2, 1.

olegd ★★★
() автор топика

Вот ты говоришь, что нужно сравнивать файлы между собой. Но есть небольшой такой нюанс/вопрос. Обычно сравнивают с эталонным файлом, он есть этот эталонный файл (например, первый в списке, или самый больший, или самый меньший)? Или этот эталонный файл ещё нужно сначала найти/сформировать, проведя анализ всех файлов на наибольшее количество совпадающих строк?

P.S. посмотри как работает «diffuse» и визуализирует результат сравнения одновременно нескольких файлов. Может это именно то, что ты хочешь?

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

Или этот эталонный файл ещё нужно сначала найти/сформировать,

Да.

посмотри как работает «diffuse»

http://diffuse.sourceforge.net/ ? Спасибо, смотрю.

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

В одном файле строки 1,2,3, а в другом — 3,2,1. Какой должен быть порядок строк в результате?

сравнивает так же, как diff, то есть общей строкой считается только 3. То есть результат будет 1, 2, 3, 2, 1.

Перечитывал мануал к diffutils в texinfo. Там написано, что с равным успехом может счесть общей любую из 3 строк. Поведение в таком случае жёстко не задано и зависит от реализации.

Но в моём случае 3, 2, 1, 2, 3 и 1, 3, 2, 3, 1 тоже будут приемлемы.

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