Сравните два файла построчно и сгенерируйте разницу в другом файле


121

Я хочу сравнить файл1 с файлом2 и создать файл3, содержащий строки в файле1, которых нет в файле2.


Я пробовал diff, но он генерирует некоторые числа и другие символы перед разными строками, что затрудняет сравнение файлов.
— Вс,

Ответы:


216

diff (1) - это не ответ, но comm (1) - это ответ.

NAME
       comm - compare two sorted files line by line

SYNOPSIS
       comm [OPTION]... FILE1 FILE2

...

       -1     suppress lines unique to FILE1

       -2     suppress lines unique to FILE2

       -3     suppress lines that appear in both files

Так

comm -2 -3 file1 file2 > file3

Входные файлы необходимо отсортировать. Если это не так, сначала отсортируйте их. Это можно сделать с помощью временного файла или ...

comm -2 -3 <(sort file1) <(sort file2) > file3

при условии, что ваша оболочка поддерживает подстановку процессов (bash поддерживает).


1
Помните, что два файла должны быть отсортированы и уникальны
— Энди

6
Вы можете сгруппировать варианты вместе:comm -23
— Paolo M

Что значит «отсортировано»? Что строки имеют одинаковый порядок? Тогда это, вероятно, нормально для большинства случаев использования - например, проверка того, какие строки были добавлены, путем сравнения с резервной более старой версией. Если вновь добавленные строки не могут находиться между существующими строками, это большая проблема.
— Егор Ганс

@EgorHans: если в файле есть, например, строки, содержащие целые числа, такие как "3 \ n1 \ n3 \ n2 \ n", строки должны быть сначала переупорядочены в порядке возрастания или убывания, например, "\ 1 \ n2 \ n3 \ n3 \ n" с дубликатами. смежны. Это «отсортировано», и оба файла должны быть отсортированы аналогичным образом. Когда в новом файле есть новые строки, не имеет значения, находятся ли они «между существующими строками», потому что после сортировки их нет, они отсортированы.
— sorpigal

48

Утилита Unix diffпредназначена именно для этой цели.

$ diff -u file1 file2 > file3

См. Руководство и Интернет для получения информации о вариантах, различных форматах вывода и т. Д.


8
Это не выполняет запрошенную работу; он вставляет кучу дополнительных символов, даже с использованием переключателей командной строки, предложенных в других ответах.
— xenocyon

20

Рассмотрим это:
файл a.txt:

abcd
efgh

файл b.txt:

abcd

Вы можете найти разницу с:

diff -a --suppress-common-lines -y a.txt b.txt

Результат будет:

efgh 

Вы можете перенаправить вывод в выходной файл (c.txt), используя:

diff -a --suppress-common-lines -y a.txt b.txt > c.txt

Это ответит на ваш вопрос:

"... который содержит строки в файле1, которых нет в файле2."


2
У этого ответа два ограничения: (1) он работает только для коротких строк (по умолчанию менее 80 символов, хотя это можно изменить) и, что более важно, (2) он добавляет «<» в конце каждого строка, которую нужно удалить другой программой (например, awk, sed).
— сергут

Во многих случаях вы также захотите использовать -d, что сделает diffвсе возможное, чтобы найти наименьшее возможное различие. -i, -E, -w, -BИ --suppress-blank-emptyтакже может быть полезно время от времени, хотя и не всегда. Если вы не знаете, что подходит вашему варианту использования, попробуйте diff --helpсначала (что обычно является хорошей идеей, если вы не знаете, что может делать команда).
— Егор Ганс

Кроме того, используя --line-format =% L, вы удерживаете diff от генерации каких-либо дополнительных символов (по крайней мере, в справке сказано, что это работает так, но мы собираемся попробовать).
— Егор Ганс

Кроме того, это короче , и , кажется , работает так же stackoverflow.com/a/27667185/1179925
— mrgloom

8

Иногда diffэто именно та утилита, которая вам нужна, но иногда joinболее подходящая. Файлы должны быть предварительно отсортированы или, если вы используете оболочку, которая поддерживает подстановку процессов, такую ​​как bash, ksh или zsh, вы можете выполнять сортировку на лету.

join -v 1 <(sort file1) <(sort file2)

За это надо получить медаль! Это было именно то, что я искал последние 2 часа
— Затарра

7

Пытаться

sdiff file1 file2

У меня это обычно работает намного лучше в большинстве случаев. Вы можете предварительно отсортировать файлы, если порядок строк не важен (например, некоторые текстовые файлы конфигурации).

Например,

sdiff -w 185 file1.cfg file2.cfg

1
Хорошая утилита! Мне нравится, как он отмечает отличительные линии. Делает сравнение конфигураций намного проще. Это вместе с sort - смертельная комбинация (например sdiff <(sort file1) <(sort file2))
— jmagnusson

3

Если вам нужно решить эту проблему с помощью coreutils, принятый ответ хорош:

comm -23 <(sort file1) <(sort file2) > file3

Вы также можете использовать sd (stream diff), который не требует сортировки или подстановки процессов и поддерживает бесконечные потоки, например:

cat file1 | sd 'cat file2' > file3

Возможно, не так много пользы в этом примере, но все же учтите это; в некоторых случаях вы не сможете использовать commни grep -Fни diff.

Вот пост в блоге, который я написал о различиях между потоками на терминале, который представляет sd.


3

Но нет grepрешения?

  • строки, которые существуют только в файле2:

    grep -Fxvf file1 file2 > file3
  • строки, которые существуют только в file1:

    grep -Fxvf file2 file1 > file3
  • строки, которые существуют в обоих файлах:

    grep -Fxf file1 file2 > file3

2

Уже много ответов, но ИМХО ни один из них не идеален. Ответ Танатоса оставляет несколько дополнительных символов в строке, а ответ Сорпигала требует, чтобы файлы были отсортированы или предварительно отсортированы, что может быть неадекватным во всех обстоятельствах.

Я думаю , что лучший способ получения линий , которые не отличаются , и больше ничего (никаких дополнительных символов, без повторного заказа) представляет собой комбинацию diff, grepи awk(или аналогичный).

Если в строках нет символа «<», коротким однострочником может быть:

diff urls.txt* | grep "<" | sed 's/< //g'

но это удалит все экземпляры «<» (меньше, чем пробел) из строк, что не всегда нормально (например, исходный код). Самый безопасный вариант - использовать awk:

diff urls.txt* | grep "<" | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}'

Этот однострочник сравнивает оба файла, затем отфильтровывает вывод diff в стиле ed, а затем удаляет завершающий "<", который добавляет diff. Это работает, даже если в самих строках есть «<».


1
comm не требует сортировки (в более новых версиях?) - просто используйте --nocheck-order. Я часто использую это при манипулировании csvs из интерфейса командной строки
— ak5 06

2

Я удивлен, что никто не упомянул diff -yо параллельном выводе , например:

diff -y file1 file2 > file3

И в file3(разные строки имеют символ |посередине):

same     same
diff_1 | diff_2

1

Используйте утилиту Diff и извлекайте только строки, начинающиеся с <в выводе


0
diff a1.txt a2.txt | grep '> ' | sed 's/> //' > a3.txt

Я перепробовал почти все ответы в этой теме, но ни один из них не был полным. После нескольких трасс выше один работал у меня. diff даст вам разницу, но с некоторыми нежелательными специальными характеристиками. где ваши фактические разностные линии начинаются с '>'. так что следующий шаг - строки grep начинаются с '>', а затем удаляются с помощью sed .


1
Это плохая идея. Вам также потребуется изменить строки, начинающиеся с <. Вы увидите это, если поменяете порядок входных файлов. Даже если бы вы сделали это, вы бы предпочли опустить grep, используя больше sed: `diff a1 a2 | sed '/> / s ///' `Это может привести к разрыву строк, содержащих >или <в правильной ситуации, и по- прежнему оставлять лишние строки, описывающие номера строк. Если вы хотите попробовать этот подход лучше всего было бы: diff -C0 a1 a2 | sed -ne '/^[+-] /s/^..//p'.
— sorpigal 06

0

Вы можете использовать diffследующее форматирование вывода:

diff --old-line-format='' --unchanged-line-format='' file1 file2

--old-line-format='', отключите вывод для файла1, если строка отличается от сравнения в файле2.
--unchanged-line-format='', отключите вывод, если строки совпадают.


0

Если у вас есть файл CSV с одним или даже несколькими столбцами , вы можете выполнять эти построчные операции «diff», используя встроенную базу данных sqlite3. Он поставляется с питоном, поэтому должен быть доступен на большинстве Linux / Mac. Вы можете создать сценарий команд sqlite3 в оболочке bash без необходимости писать python.

  1. Создайте файлы a.csv и b.csv
  2. Убедитесь, что sqlite3 установлен с помощью команды "sqlite3 -help"
  3. Выполните приведенные ниже команды непосредственно в оболочке Linux / Mac (или поместите ее в сценарий)
echo "
.mode csv
.import a.csv atable
.import b.csv btable
create table result as select * from atable EXCEPT select * from btable;
.output result.csv
select * from result ;
.quit
" | sqlite3 temp.db

Примечание: Убедитесь , что существует новая строка для каждой из команд sqlite3.

Как это устроено

  1. Импортируйте 2 CSV в таблицы «atable» и «btable» соответственно.
  2. Используйте оператор sql " except ", чтобы выбрать данные, доступные в "atable", но отсутствующие в "btable". Создайте таблицу «результатов», используя оператор select query
  3. Выведите таблицу результатов в result.csv, выполнив команду «select * from result;»

Если вам нужно работать с конкретными столбцами, вам подойдет sqlite3 или любой db.

Я пробовал различать несколько файлов GB, используя встроенные инструменты diff и comm. Sqlite на милю превосходит утилиты Linux.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.