Разница двух больших файлов

14

У меня есть "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 который не работал.

Lynob
источник
1
@ Я видел твою ссылку, и я старый участник, так что я знаю правила, но был небрежен, извини :) редактировал ее, и я увидел всплывающее окно, в котором пост был отредактирован, так что ты сделал работу для меня, и я благодарный сэр.
Линоб
50,25,125,310является общим для обоих файлов .. Вы должны удалить это из желаемого вывода ..
Heemayl
Должен ли быть сохранен порядок?
Кос
1
Это зависит от того, что вы хотите сделать с информацией, diff, IMO, для создания патча. В любом случае, теперь я уверен в вашем лучшем инструменте: diff, grep, awk или perl.
Пантера

Ответы:

20

Походит на работу для 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
terdon
источник
Вас не обманули пробелы после 200,[...]строки, а? :)
Кос
@kos нет, сначала я удалил конечные пробелы из файлов. Я предположил, что файлы ОП на самом деле не имеют их.
тердон
6

Использование 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
heemayl
источник
1
Почему catдве перенаправленные команды? Почему бы просто не запустить один, а затем другой? grep ... ; grep ...или { grep ... ; grep ... ; }если вы хотите что-то сделать с коллективным выходом.
Оли
@ Оли Спасибо .. это отличная идея .. я не думал об этом ..
Heemayl
4

Если порядок строк не имеет значения, используйте 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 находит строки, которые не соответствуют этим общим линиям.

Мур
источник
Ваша команда awk просто реализует sort | uniq -u, что дает неправильный ответ, если один файл содержит повторяющиеся строки. Для grep я бы сказал «внутренний» / «внешний», а не «внутренний» / «внешний».
Питер Кордес
@PeterCordes да, да, и кто ты такой, чтобы говорить, что это неправильный результат?
Муру
Неправильно в том смысле, что это не совсем то, о чем спрашивал вопрос, в этом угловом случае. Это может быть тем, что кто-то хочет, но вы должны указать на разницу между тем, что вы awkбудете печатать, и тем, что будут печатать ответы comm -3и diffответы.
Питер Кордес,
@PeterCordes еще раз, кто ты такой, чтобы говорить это? Пока ОП не говорит, что это то, что они хотят, мне все равно, если результат отличается от comm -3. Я не вижу причин, почему я должен это объяснять. Если вы хотите редактировать в заметке, не стесняйтесь.
Муру
ОП сказал, что хочет разницу. Это не всегда то, что производит ваша программа. Программа, которая выдает тот же вывод для одного тестового примера, но не удовлетворяет описанию, как написано для всех случаев, требует хедс-апа. Я здесь, чтобы сказать это, и это правда, независимо от того, кто я или кто ты. Я добавил заметку.
Питер Кордес
4

Используйте --*-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.

Volker Siegel
источник
3

Поскольку заказ не нужно сохранять, просто:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: слияния и сортировки test1.csvиtest2.csv
  • uniq -u: печатает только те строки, которые не имеют дубликатов
кос
источник
Это не работает, если один файл содержит строку два раза, что не отображается в другом файле. Оба случая будут в diffрезультате.
Фолькер Сигел