Разница двух больших файлов
У меня есть "test1.csv", и он содержит
200,400,600,800
100,300,500,700
50,25,125,310
и test2.csv, и он содержит
100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5
сейчас
diff test2.csv test1.csv > result.csv
отличается от
diff test1.csv test2.csv > result.csv
Я не знаю, какой правильный порядок, но я хочу что-то еще, обе команды выше будут выводить что-то вроде
2 > 100,4,2,1,7
3 2,3c3,5
4 < 100,300,500,700
5 < 50,25,125,310
6 \ No newline at end of file
7 ---
8 > 21,22,23,24,25
9 > 50,25,125,310
Я хочу вывести только разницу, поэтому results.csv должен выглядеть следующим образом
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
Я старался diff -q
а также diff -s
но они не добились цели. Порядок не имеет значения, важно то, что я хочу видеть только разницу, ни> ни <, ни пробел.
grep -FvF
сделал трюк с меньшими файлами, а не с большими
Первый файл содержит более 5 миллионов строк, второй файл содержит 1300.
в результате файл result.csv должен содержать ~4 998 700 строк
Я тоже пробовал grep -F -x -v -f
который не работал.
5 ответов
Похоже, работа для comm
:
$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
Как объяснено в man comm
:
-1 suppress column 1 (lines unique to FILE1)
-2 suppress column 2 (lines unique to FILE2)
-3 suppress column 3 (lines that appear in both files)
Итак -3
означает, что будут напечатаны только строки, уникальные для одного из файлов. Однако они имеют отступ в соответствии с тем, в каком файле они были найдены. Чтобы удалить вкладку, используйте:
$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
В этом случае вам даже не нужно сортировать файлы, и вы можете упростить приведенное выше:
comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv
С помощью grep
с bash
процесс замены:
$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
Сохранить результат как results.csv
:
cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
<()
этоbash
шаблон процесса заменыgrep -vFf test2.csv test1.csv
найдет линии, уникальные только дляtest1.csv
grep -vFf test1.csv test2.csv
найдет линии, уникальные только дляtest2.csv
Наконец, мы подводим итоги
cat
Или, как предложил Оли, вы также можете использовать группирование команд:
$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
Или просто запускайте один за другим, так как они оба пишут в STDOUT, в конечном итоге они будут добавлены:
$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
Если порядок строк не имеет значения, используйте awk
или же perl
:
awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv
использование grep
чтобы получить общие линии и отфильтровать их:
grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv
Внутренний grep получает общие строки, затем внешний grep находит строки, которые не соответствуют этим общим линиям.
Использовать --*-line-format=...
варианты diff
Ты можешь сказать diff
именно то, что вам нужно - объяснено ниже:
diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt
Можно указать вывод diff очень детально, аналогично printf
числовой формат.
Строки из первого файла, test1.csv
называются "старыми" линиями, а строки из второго, test2.csv
Есть "новые" строки. Это имеет смысл, когда diff
используется, чтобы увидеть, что изменилось в файле.
Опции, которые нам нужны, это те, которые устанавливают формат для "старых" строк, "новых" строк и "неизмененных" строк.
Форматы, которые нам нужны, очень просты:
Для измененных строк, новых и старых, мы хотим вывести только текст строк. %L
является символом формата для текста строки.
Для неизмененных строк мы ничего не хотим показывать.
При этом мы можем написать такие параметры, как --old-line-format='%L'
и сложите все вместе, используя данные вашего примера:
$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5
Примечания по производительности
Поскольку файлы имеют разный размер, попробуйте обменять входные файлы, если это не имеет значения, возможно, внутренняя работа diff
может справиться с одним способом лучше, чем с другим. Лучше либо меньше памяти, либо меньше вычислений.
Существует возможность оптимизации использования diff
с большими файлами: --speed-large-files
, Он использует предположения о файловой структуре, поэтому неясно, поможет ли это в вашем случае, но стоит попробовать.
Параметры формата описаны в man diff
под --LTYPE-line-format=LFMT
,
Поскольку заказ не нужно сохранять, просто:
sort test1.csv test2.csv | uniq -u
sort test1.csv test2.csv
: слияния и сортировкиtest1.csv
а такжеtest2.csv
uniq -u
: печатает только те строки, которые не имеют дубликатов