Jak usunąć zduplikowane linie za pomocą awk, zachowując puste linie?

13

Poniższe awkpolecenie usuwa wszystkie zduplikowane linie, jak wyjaśniono tutaj :

awk '!seen[$0]++'

Jeśli tekst zawiera puste linie, wszystkie puste linie oprócz jednej zostaną usunięte.

Jak mogę zachować wszystkie puste linie, jednocześnie usuwając wszystkie niepuste duplikaty, używając tylko awk? Dołącz także krótkie wyjaśnienie.

Serge Stroobandt
źródło

Odpowiedzi:

28

Inną opcją jest sprawdzenie NF, np .:

awk '!NF || !seen[$0]++'
Thor
źródło
11

Alternatywnie

awk '!/./ || !seen[$0]++' file

Główna sztuczka jest taka sama, seen[$0]++tworzy pozycję w seentablicy asocjacyjnej, której kluczem jest bieżąca linia ( $0). Dlatego też !seen[$0]++będzie fałszem, jeśli ta linia została już wyświetlona. /./Jest sprawdzenie, czy linia zawiera żadnych znaków spoza puste, więc !/./pasuje niż linie puste. W połączeniu z || !seen[$0]++nim zignoruje wszystkie zduplikowane linie oprócz pustych i wydrukuje resztę.

terdon
źródło
Myślę, że to powinna być zaakceptowana odpowiedź. +1 za wyjaśnienie!
SS Anne
5
awk '/^[[:blank:]]*$/ { print; next; }; !seen[$0]++'

Wszystko, co musisz zrobić, to najpierw sprawdzić pustą (naprawdę pustą lub tylko pustą) linię.

Hauke ​​Laging
źródło
5

Oto inne awkrozwiązanie, podobne do odpowiedzi @ Thora, mniej zwięzłe, ale bardziej wydajne:

awk '!NF {print;next}; !($0 in a) {a[$0];print}' file

Dzięki temu sprawdzamy tylko, czy a[$0]istnieje, czy nie. Jeśli nie, zainicjuj go, a następnie wydrukuj. W tym przypadku nie mamy żadnego odniesienia ani przypisania, a[$0]jeśli istniało.

Cuonglm
źródło
Nie zmierzyłem żadnej znaczącej różnicy czasu w moim pliku testowym o długości 288 linii. Jednak Twój kod z pewnością przyciąga nagrodę za najbardziej czytelny.
Serge Stroobandt,