Порівняйте два файли по черзі та генеруйте різницю в іншому файлі


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 does).


1
Пам'ятайте, що два файли мають бути відсортовані та унікальні
Andy

6
Можна згрупувати параметри разом:comm -23
Paolo M

Що означає "сортування"? Що рядки мають однаковий порядок? Тоді це, мабуть, добре для більшості випадків використання - як, наприклад, перевірка, які рядки були додані, порівнявши із резервною копією старішої версії. Якщо щойно додані рядки не можуть бути між існуючими рядками, це більше питання.
Єгор Ганс

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

48

Утиліта Unix diffпризначена саме для цієї мети.

$ diff -u file1 file2 > file3

Перегляньте посібник та Інтернет для параметрів, різних форматів виводу тощо.


8
Це не виконує задану роботу; він вставляє купу зайвих символів, навіть із використанням перемикачів командного рядка, запропонованих в інших відповідях.
ксеноцион

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

Це відповість на ваше запитання:

"... який містить рядки у file1, які відсутні у file2."


2
У цій відповіді є два обмеження: (1) він працює лише для коротких рядків (за замовчуванням менше 80 символів, хоча це можна змінити), і, що ще важливіше, (2) додавання "<" в кінці кожного рядок, який потрібно забрати за допомогою іншої програми (наприклад, awk, sed).
сергут

У багатьох випадках ви також хочете використовувати -d, що зробить diffвсе можливе, щоб знайти найменшу можливу різницю. -i, -E, -w, -BІ --suppress-blank-emptyтакож може бути корисно час від часу, хоча і не завжди. Якщо ви не знаєте, що відповідає вашому випадку використання, спробуйте diff --helpспочатку (що, як правило, непогана ідея, коли ви не знаєте, що може зробити команда).
Єгор Ганс

Крім того, використовуючи --line-format =% L, ви не відрізняєтеся від генерування зайвих символів (принаймні, у довідці йдеться про те, що він працює так, але ще збирається його спробувати).
Єгор Ганс

Крім того, це коротше , і , здається , працює так само stackoverflow.com/a/27667185/1179925
mrgloom

8

Іноді diffпотрібна утиліта, але іноді joinє більш доцільною. Файли потрібно заздалегідь відсортувати або, якщо ви використовуєте оболонку, яка підтримує процес заміщення, такі як bash, ksh або zsh, ви можете робити сортування на льоту.

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

Ви повинні отримати медаль за це! Це було саме те, що я шукав останні 2 години
Zatarra

7

Спробуйте

sdiff file1 file2

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

Наприклад,

sdiff -w 185 file1.cfg file2.cfg

1
Приємна утиліта! Мені подобається, як це відмічає відмінні лінії. Полегшує порівняння конфігурацій. Це разом із сортом є смертельним комбо (наприклад 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рішення немає ?

  • рядки, які існують лише у file2:

    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}'

Цей однолінійний файл відрізняє обидва файли, потім відфільтровує виведення стилю ed у diff, а потім видаляє контур "<", який додає diff. Це працює, навіть якщо рядки містять самі "<".


1
comm не потребує сортування (у новіших версіях?) - просто скористайтесь --nocheck-order. Я дуже використовую це під час маніпулювання csvs з CLI
ak5

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

Я спробував майже всі відповіді в цій темі, але жодна не була повною. Після кількох стежок вище один працював на мене. різниця дасть вам різницю, але з деякими небажаними особливими властивостями. де ви фактично різницеві лінії починаються з '>'. тому наступним кроком є обв’язування ліній, починається з '>', а потім видалення таких же з sed .


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

0

Ви можете використовувати diffнаступне форматування виводу:

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

--old-line-format='', вимкніть вихід для file1, якщо рядок відрізнявся порівняйте у file2.
--unchanged-line-format='', вимкнути вихід, якщо рядки були однаковими.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.