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


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.