Mam dwa duże pliki (zestawy nazw plików). Około 30 000 linii w każdym pliku. Próbuję znaleźć szybki sposób znajdowania wierszy w pliku 1, które nie występują w pliku 2.
Na przykład, jeśli jest to plik1:
line1
line2
line3
A to jest plik2:
line1
line4
line5
Zatem mój wynik / wynik powinien wynosić:
line2
line3
To działa:
grep -v -f file2 file1
Ale jest bardzo, bardzo wolny, gdy jest używany na moich dużych plikach.
Podejrzewam, że jest dobry sposób na wykonanie tego za pomocą diff (), ale dane wyjściowe powinny być tylko liniami, niczym więcej, i nie mogę znaleźć przełącznika do tego.
Czy ktoś może mi pomóc znaleźć szybki sposób na zrobienie tego, używając bash i podstawowych plików binarnych z linuksem?
EDYCJA: Aby odpowiedzieć na moje pytanie, jest to najlepszy sposób, jaki do tej pory znalazłem za pomocą diff ():
diff file2 file1 | grep '^>' | sed 's/^>\ //'
Z pewnością musi być lepszy sposób?
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt
cat file1 file2 file2 | sort | uniq --unique
patrz moja odpowiedź poniżej.Odpowiedzi:
Możesz to osiągnąć kontrolując formatowanie starych / nowych / niezmienionych linii w
diff
danych wyjściowych GNU :Pliki wejściowe należy posortować, aby to zadziałało. Za pomocą
bash
(izsh
) możesz sortować na miejscu z podstawieniem procesu<( )
:W powyższym nowym i niezmienionym wierszu są pomijane, więc wyprowadzane są tylko zmienione (tj. Usunięte linie w twoim przypadku). Można również skorzystać z kilku
diff
opcji, że inne rozwiązania nie oferują, takie jak-i
ignorowanie wielkości liter lub różne opcje whitespace (-E
,-b
,-v
etc) dla mniej ścisłe dopasowanie.Wyjaśnienie
Opcje
--new-line-format
,--old-line-format
i--unchanged-line-format
pozwalają kontrolować sposóbdiff
formatuje różnice, podobne doprintf
formatu specyfikatorami. Te opcje formatują odpowiednio nowe (dodane), stare (usunięte) i niezmienione linie. Ustawienie pustego „” zapobiega wyjściu tego rodzaju linii.Jeśli znasz zunifikowany format różnic , możesz go częściowo odtworzyć za pomocą:
Specyfikatorem
%L
jest linia, o której mowa, i każdy z nich poprzedza „+” „-” lub „”, podobnie jakdiff -u
(zauważ, że wyświetla tylko różnice, brakuje linii---
+++
i@@
na górze każdej zgrupowanej zmiany). Można również użyć tego robić inne przydatne rzeczy jak liczba każdej linii z%dn
.diff
Metoda (wraz z innymi sugestiamicomm
ijoin
) produkują tylko oczekiwane wyjście z posortowanej wejścia, choć można użyć<(sort ...)
do sortowania w miejscu. Oto prostyawk
(nawk) skrypt (zainspirowany skryptami połączonymi w odpowiedzi Konsolebox), który akceptuje arbitralnie uporządkowane pliki wejściowe i wyświetla brakujące wiersze w kolejności, w jakiej występują w pliku1.To przechowuje całą zawartość pliku1 linia po linii w tablicy indeksowanej numerem wiersza
ll1[]
oraz całą zawartość pliku2 linia po linii w tablicy asocjacyjnej indeksowanej treści liniiss2[]
. Po odczytaniu obu plików, iterujll1
i użyjin
operatora, aby ustalić, czy wiersz w pliku 1 jest obecny w pliku 2. (Będzie to miało inny wynik niżdiff
metoda, jeśli istnieją duplikaty).W przypadku, gdy pliki są wystarczająco duże, aby ich przechowanie powodowało problem z pamięcią, możesz wymienić procesor na pamięć, przechowując tylko plik 1 i usuwając dopasowania podczas odczytu pliku 2.
Powyżej przechowuje całą zawartość pliku1 w dwóch tablicach, jedna indeksowana według numeru linii
ll1[]
, druga indeksowana według zawartości liniiss1[]
. Następnie, gdy plik2 jest czytany, każda pasująca linia jest usuwana zll1[]
iss1[]
. Na koniec wyprowadzane są pozostałe wiersze z pliku1, zachowując pierwotną kolejność.W tym przypadku, przy opisanym problemie, możesz także dzielić i podbijać za pomocą GNU
split
(filtrowanie jest rozszerzeniem GNU), powtarzane przebiegi z fragmentami pliku 1 i całkowite czytanie pliku 2 za każdym razem:Zwróć uwagę na użycie i umiejscowienie
-
znaczeniastdin
wgawk
wierszu poleceń. Zapewnia tosplit
plik file1 w porcjach po 20000 linii na wywołanie.Dla użytkowników systemów innych niż GNU, to prawie na pewno coreutils GNU pakiet można uzyskać, w tym na OSX jako część firmy Apple Xcode narzędzi GNU, która przewiduje
diff
,awk
choć tylko POSIX / BSDsplit
zamiast wersji GNU.źródło
diff
: ogólnie pliki wejściowe będą inne,diff
w tym przypadku zwracane jest 1 . Rozważ to jako bonus ;-) Jeśli testujesz w skrypcie powłoki 0 i 1 są oczekiwanymi kodami wyjścia, 2 oznacza problem.man diff
. Dzięki!Polecenie comm (skrót od „common”) może być przydatne
comm - compare two sorted files line by line
man
Plik jest całkiem czytelny dla tego produktu.źródło
comm
ma również opcję sprawdzenia, czy dane wejściowe są posortowane--check-order
(co wydaje się tak czynić, ale ta opcja spowoduje błąd zamiast kontynuować). Ale aby posortować pliki, po prostu zrób:com -23 <(sort file1) <(sort file2)
i tak dalejcomm
w ogóle nie działa. Dopiero po chwili zrozumiałem, że chodzi o zakończenia linii: nawet linie wyglądające identycznie są uważane za różne, jeśli mają różne zakończenia linii. Poleceniados2unix
można użyć do konwersji zakończeń linii CRLF tylko na LF.Jak sugeruje konsolebox, rozwiązanie grep dla plakatów
faktycznie działa świetnie (szybko), jeśli po prostu dodasz
-F
opcję, aby traktować wzorce jako stałe ciągi zamiast wyrażeń regularnych. Sprawdziłem to na parze ~ 1000 list plików linii, które musiałem porównać. Z-F
tym zajęło 0,031 s (rzeczywiste), podczas gdy bez zabrało 2,278 s (rzeczywiste), gdy przekierowanie wyjścia grep dowc -l
.Testy te obejmowały także
-x
przełącznik, który jest niezbędną częścią rozwiązania w celu zapewnienia całkowitej dokładności w przypadkach, gdy plik2 zawiera wiersze, które pasują do części, ale nie wszystkich, jednego lub więcej wierszy w pliku1.Tak więc rozwiązaniem, które nie wymaga sortowania danych wejściowych, jest szybkie, elastyczne (rozróżnianie wielkości liter itp.) To:
Nie działa to ze wszystkimi wersjami grep, na przykład nie działa w systemie macOS, gdzie linia w pliku 1 będzie pokazana jako nieobecna w pliku 2, nawet jeśli tak jest, jeśli pasuje do innej linii, która jest jego podciągiem . Alternatywnie możesz zainstalować GNU grep na macOS , aby skorzystać z tego rozwiązania.
źródło
-F
tym nie skaluje się dobrze.file2
.-x
opcją najwyraźniej zużywa więcej pamięci. Zfile2
zawierających 180M słów 6-10 bajtów mój proces dostałKilled
na maszynie RAM 32GB ...jaka jest prędkość sortowania i porównywania?
źródło
Jeśli masz mało „narzędzi fantazyjnych”, na przykład w pewnym minimalnym dystrybucji Linuksa, istnieje rozwiązanie z właśnie
cat
,sort
iuniq
:Test:
Jest to również stosunkowo szybkie w porównaniu do
grep
.źródło
--unique
opcji. Powinieneś być w stanie użyć standardowej opcji POSIX do tego:| uniq -u
seq 1 1 7
tworzy liczby od 1, z przyrostem 1, do 7, tj. 1 2 3 4 5 6 7. I właśnie są twoje 2!-t
Pilnuje, że porównuje całą linię, jeśli miał miejsce w niektórych liniach.źródło
comm
,join
wymaga posortowania obu linii wejściowych w polu, w którym wykonuje się operację łączenia.Możesz użyć Pythona:
źródło
Korzystać
combine
zmoreutils
pakietu, narzędzie, które obsługuje zestawynot
,and
,or
,xor
operacjetzn. podaj mi wiersze, które są w pliku 1, ale nie w pliku 2
LUB podaj mi linie w pliku 1 minus linie w pliku 2
Uwaga:
combine
sortuje i wyszukuje unikalne linie w obu plikach przed wykonaniem jakiejkolwiek operacji, alediff
nie robi tego. Więc możesz znaleźć różnice między danymi wyjściowymidiff
acombine
.Więc w rzeczywistości mówisz
Znajdź różne linie w pliku 1 i 2, a następnie podaj mi linie w pliku 1 minus linie w pliku 2
Z mojego doświadczenia wynika, że jest znacznie szybszy niż inne opcje
źródło
Pomocne może być użycie fgrep lub dodanie opcji -F do grep. Ale do szybszych obliczeń możesz użyć Awk.
Możesz wypróbować jedną z następujących metod Awk:
http://www.linuxquestions.org/questions/programming-9/grep-for-huge-files-826030/#post4066219
źródło
Zwykle robię to przy użyciu
--suppress-common-lines
flagi, ale pamiętaj, że działa to tylko wtedy, gdy robisz to w formacie side-by-side.diff -y --suppress-common-lines file1.txt file2.txt
źródło
Przekonałem się, że użycie normalnej instrukcji if i for loop działało idealnie.
źródło
grep
wyników zostanie rozwinięty do wielu słów lub jeśli którykolwiek z twoichfile2
wpisów może być traktowany przez powłokę jako glob.