Czy istnieje narzędzie do uzyskiwania wierszy w jednym pliku, których nie ma w innym?

Odpowiedzi:

159

Tak. Za pomocą standardowego grepnarzędzia do wyszukiwania plików w poszukiwaniu ciągów tekstowych można odjąć wszystkie wiersze jednego pliku od drugiego.

grep -F -x -v -f fileB fileA

Działa to poprzez użycie każdej linii w pliku B jako wzorca ( -f fileB) i traktowanie go jako zwykłego ciągu pasującego do siebie (nie zwykłego wyrażenia regularnego) ( -F). Zmuszasz dopasowanie do całej linii ( -x) i wypisujesz tylko te linie, które nie pasują ( -v). Dlatego drukujesz linie w pliku A, które nie zawierają tych samych danych, co dowolna linia w pliku B.

Minusem tego rozwiązania jest to, że nie bierze ono pod uwagę kolejności linii, a jeśli dane wejściowe mają zduplikowane linie w różnych miejscach, możesz nie otrzymać tego, czego oczekujesz. Rozwiązaniem tego jest użycie prawdziwego narzędzia porównywania, takiego jak diff. Możesz to zrobić, tworząc plik różnicowy o wartości kontekstu na 100% linii w pliku, a następnie analizując go pod kątem tylko linii, które zostałyby usunięte w przypadku konwersji pliku A do pliku B. (Uwaga: to polecenie usuwa również różnicę formatowanie po uzyskaniu właściwych wierszy).

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC
Caleb
źródło
@ inderpreet99 Argument małej litery -ufaktycznie zajmuje parametr liczby, o ile nie następuje po nim spacja. Zaletą tego, co miałem wcześniej, jest to, że będzie działać z wartością lub bez, więc możesz użyć czegoś w tej procedurze podrzędnej, która nie zwraca danych wyjściowych. Z drugiej strony wielkie litery „-U” wymagają argumentu.
Caleb
bądź ostrożny, grep -f to O (N ^ 2) Wierzę: stackoverflow.com/questions/4780203/…
rogerdpack
1
diffrurociąg działa wspaniale dzięki.
Felipe Alvarez
Aby uwzględnić problem z sortowaniem, można użyć substytucji procesu w poleceniu, aby przetworzyć każdy plik przed greppotrzebą. Przykład:grep -F -x -v -f <(sort fileB) <(sort fileA)
Tony Cesaro
@TonyCesaro To działałoby, jeśli Twój zestaw danych nie jest określony dla konkretnego zamówienia, a duplikaty nie muszą być brane pod uwagę. Zaletą używania diffjest to, że pozycja w pliku jest brana pod uwagę.
Caleb
57

Odpowiedź zależy w dużej mierze od rodzaju i formatu porównywanych plików.

Jeśli porównywane pliki są posortowanymi plikami tekstowymi, narzędzie GNU napisane przez Richarda Stallmana i Davide McKenzie commmoże wywołać filtrowanie, którego szukasz. Jest częścią coreutils.

Przykład

Załóżmy, że masz 2 następujące pliki:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Linie w pliku b, których nie ma w pliku a:

$ comm <(sort a) <(sort b) -3
    6
Przyjaciel
źródło
1
+1 za wzmiankę comm; niestety commwymaga posortowanych plików
Arcege
11
więc posortuj je? comm <(sort a) <(sort b) -1 -2
Sirex
To dziwna składnia. <()? Działa i rozumiem, ale czy istnieje nazwa tej dziwności?
mlissner,
2
@mlissner <()jest również znany jako podstawienie procesu .
miku
1
commzostał pierwotnie napisany około 1973 roku przez kogoś z Bell Labs, a nie rms. Mówisz o implementacji GNU, która pojawiła się dużo później. Przez lata istniało wiele różnych implementacji narzędzi uniksowych.
Stéphane Chazelas
32

z przepełnienia stosu ...

comm -23 plik1 plik2

-23 pomija wiersze znajdujące się w obu plikach lub tylko w pliku 2. Pliki muszą zostać posortowane (są one w twoim przykładzie), ale jeśli nie, najpierw przeprowadź je przez sortowanie ...

Zobacz stronę podręcznika tutaj

JJS
źródło
To nie działa na mnie z jakiegoś powodu ...
sty
@Jan są sortowane twoje pliki? Jak je posortowałeś?
JJS
8

Metody grep i comm (z sortowaniem) zajmują dużo czasu w przypadku dużych plików. SiegeX i ghostdog74 udostępniają dwie świetne metody awk do wyodrębniania linii unikatowych dla jednego z dwóch plików w przepełnieniu stosu:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
Miles Wolbe
źródło
2
Jeśli robisz to z dużymi plikami, ograniczenia pamięci związane z ładowaniem dużego pliku do tablicy asocjacyjnej będą zabronione.
Charles Duffy
4

Jeśli pliki są duże i nie masz niestandardowego porządku dla swoich wpisów, grep trwa o wiele za długo. Szybka alternatywa byłaby

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[plik2-plik1 wyniki do ekranu, potok do pliku itp.]

Zmiana >na <uzyskałaby przeciwne odjęcie.rm 1 2

Eshel Faraggi
źródło
2

Możesz również rozważyć vimdiff, to podkreśla różnice między plikami w edytorze vim

simona
źródło
1
Ale czy jest prosty sposób na automatyczne odejmowanie w Vimdiff?
Kazark