Czy sed może usunąć „podwójne” znaki nowej linii?

25

Mam dokument z wieloma pustymi wierszami.

Jak mogę je usunąć, gdy są razem 2 lub więcej.

Próbowałem sed "s/\n\n//"pliku, ale to nie działało. Żaden błąd.

Michael Durrant
źródło
3
Czy czytam cię poprawnie, jeśli nie chcesz usunąć wszystkich pustych wierszy, ale tylko jeśli są to dwa lub więcej. Więc nie ma pojedynczych pustych linii?
Runium
1
A jeśli to dwie lub więcej linii, czy naprawdę należy je wszystkie usunąć, czy tylko wszystkie oprócz jednej?
Hauke ​​Laging

Odpowiedzi:

42

Aby usunąć puste linie:

sed  '/^$/d'

sedjest zorientowany na linię, więc myślenie w kategoriach „2 lub więcej określonego bajtu” działa, z wyjątkiem sytuacji, gdy ten bajt jest znakiem nowej linii. Następnie musisz wymyślić coś, co zadziała dla całej linii.

Bruce Ediger
źródło
Oczywiście! +1 za prostą elegancję.
terdon
2
sedjest w stanie obsłużyć kilka linii za pomocą funkcji „przestrzeni wzorów” / „przestrzeni trzymania” Ale czuję, że to zbyt skomplikowane. ;-)
Hauke ​​Laging
Nie będzie działać zgodnie z oczekiwaniami, jeśli pierwszym znakiem pliku jest nowa linia.
Chris Down,
1
Aby to działało, gdy pierwszy znak jest znak nowej linii (jeśli jest to naprawdę to wymóg), to można ująć polecenia z adresem ujemnej 1!(mecz wszystko z wyjątkiem linii nr 1), w następujący sposób: sed '1!{/^$/d'}.
Toby Speight
1
@AaronFranke - tak, ale jest to aspekt tego, jak powłoki Linux traktują przekierowanie „>”. Powłoka patrzy na wiersz poleceń, widzi przekierowanie standardowego wejścia do pliku, tworzy ten plik i dopiero wtedy uruchamia się sed. Utworzenie pliku spowoduje usunięcie dowolnego istniejącego pliku o tej samej nazwie. sed '/^&/d' file.txt > otherfile.txtbędzie działać.
Bruce Ediger,
24

Nie ma potrzeby sed. grepzrobi:

grep .

(to grepznaczy SPC, kropka, czyli pasuje do dowolnej linii zawierającej co najmniej jeden znak).

Jest także:

tr -s '\n'

(ściśnij dowolną sekwencję znaków nowej linii w jedną).

Jak zauważył Chris, oba nie są równoważne, ponieważ usuwanie pustych linii (jak pierwsze rozwiązanie powyżej i większość innych odpowiedzi skupia się tutaj) nie jest tym samym, co ściskanie sekwencji znaków nowego wiersza zgodnie z żądaniem w przypadku, gdy pierwszy wiersz jest pusty, ponieważ zajmuje tylko jeden wiodący znak nowej linii, aby pierwszy wiersz był pusty.

Stéphane Chazelas
źródło
2
Nie będzie działać zgodnie z oczekiwaniami, jeśli pierwszym znakiem pliku jest nowa linia: sprunge.us/FLAJ
Chris Down
7

sednie jest najlepszym narzędziem do tego, ponieważ jest oparty na linii i traktuje \njako znak końca linii, co komplikuje się.Widząc @Bruce EDIGER za odpowiedź sed może okazać się doskonałym narzędziem do pracy, nadal, oto niektóre inne opcje:

  1. Perl

    perl -ne 'print if /./' file.txt
    

    lub

    perl -pe '$/=""; s/\n+/\n/;' file.txt 
    

    Dzięki @ruakh, który zmusił mnie do przeczytania tego :

    $ /

    Separator rekordów wejściowych, domyślnie nowy wiersz. Wpływa to na wyobrażenie Perla o tym, czym jest „linia”. Działa jak zmienna RS awk, włączając traktowanie pustych linii jako terminatora, jeśli jest ustawiony na ciąg pusty (pusty wiersz nie może zawierać spacji ani tabulatorów). Możesz ustawić ciąg znaków składający się z wielu znaków, aby dopasować terminator zawierający wiele znaków lub cofnąć, aby odczytać koniec pliku. Ustawienie go na „\ n \ n” oznacza coś nieco innego niż ustawienie na „”, jeśli plik zawiera kolejne puste wiersze. Ustawienie „” potraktuje dwie lub więcej następujących po sobie pustych linii jako pojedynczą pustą linię. Ustawienie na „\ n \ n” będzie ślepo zakładać, że następny znak wejściowy należy do następnego akapitu, nawet jeśli jest to nowy wiersz.

  2. gawk / awk

    awk '$1' file.txt
    

    Będzie to działać w opublikowanym przykładzie, ale jak wskazał @Stephane Chazelas , usunie również wiersze, których pierwsze pole „wygląda” 0. Jest to bardziej niezawodne:

    awk NF file.txt
    
terdon
źródło
W przypadku Perla perl -pe 's/\n+/\n/ file.txtseparator rekordów wejściowych nie ma znaczenia dla tego zastosowania.
vonbrand
@ vonbrand no perl -pelub perl -nepraca linia po linii. \n+nigdy nie będzie pasować, ponieważ jest stosowany tylko w jednym wierszu. Dlatego trzeba albo ustawić $/lub użyj -0ti slurp plik całości: perl -0pe 's/\n+/\n/' file.
terdon
6

Co masz na myśli usunąć? usunąć duplikat (wiele pustych wierszy do jednego) czy usunąć wszystkie?

Jeśli chcesz usunąć duplikat, oto metoda wykorzystująca sed:

sed '$!N; /^\(.*\)\n\1$/!P; D'

Symuluje uniqpolecenie.

Najlepszym wyborem jest użycie awk:

awk NF <filename>
Cuonglm
źródło
Ta sedczęść działa świetnie! Polecając ten jako najlepszą odpowiedź.
Akito
2

W przypadku większości tych odpowiedzi należy najpierw usunąć końcowe białe znaki. Usunięcie podwójnych linii nowych usuwa wszystkie puste linie. (Pomyśl o tym).

Dosłownie zinterpretowana OP chce „usunąć wszystkie puste linie z pliku, jeśli występują powtarzające się puste linie”.

Typowy użytkownik chce „usunąć tylko zduplikowane puste linie”.

Aby to zrobić, najpierw usuń końcowe białe spacje i potokuj przez cat -s

sed  s/[[:space:]]*$// | cat -s

A jednak nie usunie to zbędnej początkowej lub końcowej pustej linii.

mckenzm
źródło
Przesłuchany, ale to wyraźnie działa? Bez komentarza ?
mckenzm,
1
Głosowałem za ... no wiesz ... odpowiadając na pytanie. =) Nie mogę uwierzyć, że odpowiedź Bruce'a Edigera została pozytywnie oceniona, gdy usuwa każdą pustą linię. Jeśli ktoś zapyta, jak usunąć zduplikowane puste linie, nie wyobrażam sobie żadnego scenariusza, w którym usunięcie wszystkich pustych linii byłoby akceptowalnym rozwiązaniem. Ale cokolwiek. Nawiasem mówiąc
Todd Walton
2

Jeśli chcesz zachować jedną pustą linię dla dowolnej sekwencji pustych linii, możesz:

sed -e '/./b' -e :n -e 'N;s/\n$//;tn'
mikeserv
źródło
1
Jest to jedyna odpowiedź (poza tym cat -s), która faktycznie spełnia dokładnie to pytanie, jakie rozumiem. (I to jest lepsze niż cat -sdlatego, że mogę sed -iz tym korzystać.)
Matthew
-2

Spróbuj sed -e 's#\\n\\n#\\n#g' input.file > output.fileużyć /obu jako separatora pól, a część wyrażenia regularnego może być problemem.

linuxrebel
źródło
2
Właśnie nadałem temu wir z jednym z moich plików zawierających podwójne i potrójne znaki nowej linii w sekwencji. W ogóle nie działa dla mnie.
składniaerror
-3

Użyj tego polecenia:

tr -s '\r' '\n'
miauczeć
źródło
tak, ich odpowiedź nie działała dla mnie.
miau
5
AFAIK ta odpowiedź jest niepoprawna. Polecam usunąć go.
zuazo
och, to dlatego, że mój plik zawiera wiele znaków nowego wiersza i zwrotów karetki. 0x0d0a
meow
2
W rzeczywistości polecenie usuwa powtarzające się linie z końcem linii systemu Windows. Przetestuj za pomocą echo -e 'one\r\n\r\n\r\n\rtwo'| tr -s '\r' '\n'. Polecenie trprzetłumaczy wszystko \rna, \na następnie ścisnie wszystko \ndo jednego. Więc działa, nie wiem, co zrobić z faktem, że dotyczy to systemu Windows, a nie UNIX.