jak pokazać wspólne linie (reverse diff)?

170

Mam serię plików tekstowych, dla których chciałbym znać wiersze wspólne, a nie różne między nimi. Unix lub Windows z linii poleceń są w porządku.

bla:

linux-vdso.so.1 =>  (0x00007fffccffe000)
libvlc.so.2 => /usr/lib/libvlc.so.2 (0x00007f0dc4b0b000)
libvlccore.so.0 => /usr/lib/libvlccore.so.0 (0x00007f0dc483f000)
libc.so.6 => /lib/libc.so.6 (0x00007f0dc44cd000)

bar:

libkdeui.so.5 => /usr/lib/libkdeui.so.5 (0x00007f716ae22000)
libkio.so.5 => /usr/lib/libkio.so.5 (0x00007f716a96d000)
linux-vdso.so.1 =>  (0x00007fffccffe000)

Tak więc, biorąc pod uwagę, że te dwa pliki powyżej danych wyjściowych pożądanego narzędzia byłyby podobne file1:line_number, file2:line_number == matching text (tylko sugestia, naprawdę nie obchodzi mnie, jaka jest składnia):

foo:1, bar:3 == linux-vdso.so.1 =>  (0x00007fffccffe000)

dzięki.

Matt Wilkie
źródło
@ChristopherSchultz Mój błąd. Pierwsza linia w pierwszym przykładzie powinna odpowiadać ostatniej linii w drugim przykładzie. Dzięki za złapanie błędu; wymiana pieniędzy.
Matt Wilkie
1
Kolejne podobne pytanie z dobrymi odpowiedziami: unix.stackexchange.com/questions/1079/…
MortezaE

Odpowiedzi:

210

Na * nix możesz użyć comm . Odpowiedź na pytanie brzmi:

comm -1 -2 file1.sorted file2.sorted 
# where file1 and file2 are sorted and piped into *.sorted

Oto pełne wykorzystanie comm:

comm [-1] [-2] [-3 ] file1 file2
-1 Suppress the output column of lines unique to file1.
-2 Suppress the output column of lines unique to file2.
-3 Suppress the output column of lines duplicated in file1 and file2. 

Zauważ również, że ważne jest, aby posortować pliki przed użyciem comm, jak wspomniano na stronach podręcznika.

Dan Lew
źródło
3
comm [-1] [-2] [-3] plik1 plik2 -1 Pomija kolumnę wyjściową wierszy unikalnych dla pliku1. -2 Pomiń kolumnę wyjściową wierszy unikalnych dla pliku2. -3 Pomiń kolumnę wyjściową wierszy powielonych w plik1 i plik2.
ojblass
@ojblass: Dodano to do odpowiedzi.
Matt J,
6
Odkryłem, że ważne jest posortowanie plików przed użyciem komendy. Być może dodaj to do odpowiedzi.
Matt Wilkie
11
krótka odpowiedź na pytanie: comm -1 -2 plik1 plik2
greggles
6
Można to wykorzystać, jeśli pliki nie są klasyfikowane: komunikator -1 -2 <(porządek nazwa_pliku1) <(porządek nazwa_pliku2)
Kevin Wheeler
56

Znalazłem tę odpowiedź na pytanie wymienione jako zduplikowane . Uważam, że grep jest bardziej przyjazny dla administratora niż comm, więc jeśli chcesz tylko zestaw pasujących wierszy (przydatnych na przykład do porównywania plików CSV), po prostu użyj

grep -F -x -f file1 file2

lub uproszczona wersja fgrep

fgrep -xf file1 file2

Ponadto możesz użyć file2*do globowania i szukać linii wspólnych dla wielu plików, a nie tylko dwóch.

Inne przydatne odmiany to

  • -n flaga, aby pokazać numer każdego dopasowanego wiersza
  • -c aby policzyć tylko liczbę pasujących wierszy
  • -vaby wyświetlić tylko te wiersze w pliku2, które się różnią (lub używają diff).

Korzystanie commjest szybsze, ale odbywa się to kosztem konieczności sortowania plików. Nie jest zbyt przydatna jako „odwrotna różnica”.

Ryder
źródło
dzięki Ryder, dla wielu może to być bardziej przydatne niż komunikacja. Powinieneś podać link do odpowiedzi źródłowej (w Q w nawigacji po prawej stronie jest ponad pół tuzina linków; znalezienie trochę pracy). Byłoby również miło wiedzieć, jak dobrze grep radzi sobie z niesortowanymi lub inaczej posortowanymi danymi wejściowymi i może wypisać odpowiednie numery wierszy dopasowań.
Matt Wilkie
1
@mattwilkie Poczułem potrzebę powrotu i wyjaśnienia sposobu użycia -vflagi po tym, jak sam się z nią pomyliłem. Załóżmy, że masz dwa pliki csv plik1 i plik2 i mają one zarówno nakładające się, jak i nienakładające się wiersze. Jeśli chcesz mieć wszystkie i tylko nienakładające się wiersze, użycie fgrep -v file1 file2zwróci tylko nienakładające się wiersze w pliku2 i żadne dodatkowe nienakładające się wiersze w pliku1 . Dla niektórych może to być oczywiste, ale lepiej powiedzieć to, co oczywiste, niż ryzykować błędną interpretację. W tym konkretnym przypadku sortowanie plików i używanie commjest nadal lepszym wyborem.
Ryder
1
Dziękuję za powrót i wyjaśnienie Rydera. Dodatkowa uwaga jest zauważana i doceniana (a wszystko to zbyt łatwe, aby stare rzeczy wymknęły się!). Zmieniłem akceptowaną odpowiedź, ponieważ komunikacja jest najwyraźniej wyborem społeczności, mimo że osobiście nadal z niej korzystam, gdy sortowanie jest niepożądane.
Matt Wilkie
2
Kolejna komplikacja podczas używania grep: każda pusta linia w pierwszym pliku będzie pasować do każdej linii w drugim pliku. Upewnij się, że file1nie ma pustych wierszy, w przeciwnym razie pliki będą wyglądać tak, jakby były identyczne.
Christopher Schultz
grep -Fxfto jest dla mnie.
loxaxs
35

Zostałem tu wcześniej zapytany: Polecenie Unix do znalezienia linii wspólnych w dwóch plikach

Możesz także spróbować z perl ( tutaj jest kredyt )

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2
ChristopheD
źródło
1
dzięki. Chciałbym zaakceptować obie odpowiedzi, ponieważ perl jeden liniowiec jest cross platform. Comm dostaje skinienie głową, ponieważ jest prostszy.
Matt Wilkie
1
Idealny. Używanie terminala Cygwin w systemie Windows i commnie było łatwo dostępne. To była doskonała alternatywa.
Qix - MONICA została źle potraktowana
3
Nie ma znaczenia, w jaki sposób linie są uporządkowane. Jest dokładniejsza niż komunikacja.
powiększenie teraz
1
Wyjaśnienie jest tutaj: stackoverflow.com/questions/17552789/…
Chris Koknat
17

Właśnie nauczyłem się polecenia comm z tego wątku, ale chciałem dodać coś więcej: jeśli pliki nie są posortowane i nie chcesz dotykać oryginalnych plików, możesz potokować wyjście polecenia sortowania. To pozostawia oryginalne pliki nienaruszone. Działa w bashu, nie mogę powiedzieć o innych muszlach.

comm -1 -2 <(sort file1) <(sort file2)

Można to rozszerzyć, aby porównać dane wyjściowe polecenia zamiast plików:

comm -1 -2 <(ls /dir1 | sort) <(ls /dir2 | sort)
Greg Mueller
źródło
9

Najłatwiej to zrobić:

awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2

Pliki nie muszą być sortowane.

Gopu
źródło
1
To różni się od większości odpowiedzi tutaj, ponieważ umożliwia rekonstrukcję szablonów źródłowych. Mam dwa pliki zbudowane z tego samego opakowania, z różnymi tekstami wstawionymi w kilku punktach. Ta odpowiedź umożliwiła mi odzyskanie opakowania.
Lucas Gonze
1

Dla informacji, stworzyłem małe narzędzie dla Windows robiące to samo, co "grep -F -x -f plik1 plik2" (ponieważ nie znalazłem nic równoważnego temu poleceniu w Windows)

Oto on: http://www.nerdzcore.com/?page=commonlines

Użycie to „CommonLines plik_wejściowy1 plik_wejściowy2 plik_wyjściowy”

Kod źródłowy jest również dostępny (GPL)

Zivilyn Bane
źródło
1

W systemie Windows można użyć skryptu Powershell z CompareObject

compare-object -IncludeEqual -ExcludeDifferent -PassThru (get-content A.txt) (get-content B.txt)> MATCHING.txt | Out-Null #Find Matching Lines

PorównajObiekt:

  • IncludeEqual bez -ExcludeDifferent: wszystko
  • ExcludeDifferent bez -InclueEqual: nic
Dzierzba
źródło