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

@ 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

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


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

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


2

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


1
Ale czy jest prosty sposób na automatyczne odejmowanie w Vimdiff?
Kazark
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.