Kann sed 'doppelte' Zeilenumbrüche entfernen?

25

Ich habe ein Dokument mit vielen leeren Zeilen.

Wie kann ich sie entfernen, wenn zwei oder mehr zusammen sind?

Ich habe versucht, sed "s/\n\n//"Datei, aber es hat nicht funktioniert. Kein Fehler.

Michael Durrant
quelle
3
Lies ich Sie richtig, wenn Sie nicht alle Leerzeilen entfernen möchten, sondern nur, wenn es zwei oder mehr sind? Also keine einzelnen Leerzeilen?
Runium
1
Und wenn es zwei oder mehr Zeilen sind, sollen wirklich alle gelöscht werden oder nur alle bis auf eine?
Hauke ​​Laging

Antworten:

42

Nur um leere Zeilen zu entfernen:

sed  '/^$/d'

sedist zeilenorientiert, so dass das Denken in "2 oder mehr eines bestimmten Bytes" funktioniert, außer wenn dieses Byte eine neue Zeile ist. Dann muss man sich etwas überlegen, das für die ganze Linie funktioniert.

Bruce Ediger
quelle
Na sicher! +1 für schlichte Eleganz.
Terdon
2
sedist in der Lage, mehrere Zeilen über die Funktion "Pattern Space" / "Hold Space" zu verarbeiten. Aber ich finde das zu kompliziert. ;-)
Hauke ​​Laging
Dies funktioniert nicht wie gewünscht, wenn das erste Zeichen der Datei ein Zeilenumbruch ist.
Chris Down
1
Damit es funktioniert, wenn das erste Zeichen eine neue Zeile ist (falls dies wirklich erforderlich ist), können Sie den Befehl mit einer negativen Adresse einschließen 1!(mit Ausnahme von Zeile 1 mit allen übereinstimmen) sed '1!{/^$/d'}.
Toby Speight
1
@AaronFranke - ja, aber das ist eine Facette dessen, wie Linux-Shells die Umleitung behandeln. Die Shell sieht in der Befehlszeile nach, sieht eine Umleitung von stdout in eine Datei, erstellt diese Datei und wird erst dann ausgeführt sed. Durch das Erstellen einer Datei werden im Wesentlichen alle vorhandenen Dateien mit demselben Namen gelöscht. sed '/^&/d' file.txt > otherfile.txtwird funktionieren.
Bruce Ediger
24

Keine Notwendigkeit für sed. grepWird besorgt:

grep .

(das grepist SPC, Punkt, das entspricht jeder Zeile, die mindestens ein Zeichen enthält).

Es gibt auch:

tr -s '\n'

(Drücken Sie eine beliebige Folge von Zeilenumbrüchen zusammen).

Wie von Chris bemerkt, sind beide nicht gleichbedeutend, da das Entfernen von Leerzeilen (wie die erste Lösung oben und die meisten anderen hier behandelten Antworten) nicht dasselbe ist wie das Drücken von Folgen von Zeilenumbrüchen, wie es verlangt wird, wenn die erste Zeile leer ist Es wird nur ein führendes Zeilenumbruchzeichen benötigt, um die erste Zeile leer zu machen.

Stéphane Chazelas
quelle
2
Dies funktioniert nicht wie gewünscht, wenn das erste Zeichen der Datei eine neue Zeile ist: sprunge.us/FLAJ
Chris Down
7

sedist nicht das beste Werkzeug dafür, da es zeilenbasiert ist und \nals Zeilenendezeichen behandelt wird, was kompliziert wird.Nachdem Sie die Antwort von @Bruce Ediger gesehen haben, ist siesed möglicherweise das perfekte Werkzeug für diesen Job. Hier sind jedoch noch einige andere Optionen:

  1. Perl

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

    oder

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

    Dank @ruakh , die mich gehen und lesen Sie diese :

    $ /

    Das Trennzeichen für Eingabedatensätze, standardmäßig Newline. Dies beeinflusst Perls Vorstellung davon, was eine "Linie" ist. Funktioniert wie die RS-Variable von awk, einschließlich der Behandlung von Leerzeilen als Abschlusszeichen, wenn die Null-Zeichenfolge eingestellt ist (eine Leerzeile darf keine Leerzeichen oder Tabulatoren enthalten). Sie können eine Zeichenfolge mit mehreren Zeichen festlegen, die mit einem Abschlusszeichen mit mehreren Zeichen übereinstimmt, oder eine Undef-Zeichenfolge festlegen, um das Dateiende durchzulesen. Das Setzen auf "\ n \ n" bedeutet etwas anderes als das Setzen auf "", wenn die Datei aufeinanderfolgende Leerzeilen enthält. Wenn Sie "" einstellen, werden zwei oder mehr aufeinanderfolgende Leerzeilen als einzelne Leerzeile behandelt. Das Setzen auf "\ n \ n" setzt blind voraus, dass das nächste eingegebene Zeichen zum nächsten Absatz gehört, auch wenn es sich um eine neue Zeile handelt.

  2. gawk / awk

    awk '$1' file.txt
    

    Das wird für das Beispiel funktionieren, aber wie @Stephane Chazelas betont hat, werden auch Zeilen gelöscht, deren erstes Feld "so aussieht" 0. Das ist robuster:

    awk NF file.txt
    
terdon
quelle
Für Perl ist perl -pe 's/\n+/\n/ file.txtdas Trennzeichen für Eingabedatensätze für diese Verwendung nicht relevant.
Vonbrand
@vonbrand nein, perl -peoder perl -nezeilenweise arbeiten. \n+wird niemals übereinstimmen, da es nur auf eine einzelne Zeile angewendet wird. Deshalb sollten Sie zu jedem Satz benötigen $/oder verwenden -0ti die Datei ganze schlürfen: perl -0pe 's/\n+/\n/' file.
Terdon
6

Was meinst du entfernen? Doppelte entfernen (viele Leerzeilen zu einer) oder alle entfernen?

Wenn Sie Duplikate entfernen möchten, gehen Sie wie folgt vor:

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

Es simuliert den uniqBefehl.

Die beste Wahl ist die Verwendung von awk:

awk NF <filename>
cuonglm
quelle
Der sedTeil davon funktioniert großartig! Empfehle dieses als die beste Antwort.
Akito
2

Für die meisten dieser Antworten ist es zunächst erforderlich, nachfolgende Leerzeichen zu entfernen. Durch Entfernen doppelter Zeilenumbrüche werden alle Leerzeilen entfernt. (Denk darüber nach).

Wörtlich übersetzt will das OP "alle Leerzeilen aus einer Datei entfernen, wenn es wiederholte Leerzeilen gibt".

Der typische Benutzer möchte "nur doppelte Leerzeilen entfernen".

Um dies zu tun, entfernen Sie zuerst das nachfolgende Leerzeichen und leiten Sie es durch cat -s

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

Dabei wird jedoch keine überflüssige führende oder nachfolgende Leerzeile entfernt.

mckenzm
quelle
Abgestimmt, aber das funktioniert eindeutig? Kein Kommentar ?
McKenzm
1
Ich habe dich dafür belobigt, dass du die Frage beantwortet hast. =) Ich kann nicht glauben, dass Bruce Edigers Antwort positiv bewertet wurde, als jede leere Zeile gelöscht wurde . Wenn jemand fragt, wie doppelte Leerzeilen entfernt werden sollen, kann ich mir kein Szenario vorstellen, in dem das Löschen aller Leerzeilen eine akzeptable Lösung wäre. Aber was auch immer. Es gibt übrigens eine Seite auf der Website für sed, die dies behandelt: gnu.org/software/sed/manual/sed.html#cat-_002ds
Todd Walton
2

Wenn Sie eine einzelne Leerzeile für eine bestimmte Folge von Leerzeilen beibehalten möchten, können Sie Folgendes tun:

sed -e '/./b' -e :n -e 'N;s/\n$//;tn'
mikeserv
quelle
1
Dies ist die einzige Antwort (nebenbei cat -s), die tatsächlich genau das leistet, was die Frage gestellt hat, so wie ich es verstehe. (Und es ist besser, als cat -sweil ich damit umgehen sed -ikann.)
Matthew
-2

Versuchen Sie sed -e 's#\\n\\n#\\n#g' input.file > output.file, /beide als Feldtrennzeichen zu verwenden, und ein Teil Ihres regulären Ausdrucks könnte das Problem sein.

linuxrebel
quelle
2
Wirbelte dies einfach mit einer meiner Dateien durch, die doppelte und dreifache Zeilenumbrüche in einer Sequenz enthielten. Funktioniert überhaupt nicht für mich.
Syntaxfehler
-3

Verwenden Sie diesen Befehl:

tr -s '\r' '\n'
Miau
quelle
Ja, ihre Antwort hat bei mir nicht funktioniert.
Miau
5
AFAIK diese Antwort ist falsch. Ich empfehle Ihnen, es zu löschen.
Zuazo
Oh, das liegt daran, dass meine Datei viele Zeilenumbrüche und Zeilenumbrüche enthält. 0x0d0a
miauen
2
Tatsächlich entfernt der Befehl wiederholte Zeilen mit dem Ende des Fensters. Testen Sie mit echo -e 'one\r\n\r\n\r\n\rtwo'| tr -s '\r' '\n'. Der Befehl trübersetzt alle \rzu \nund drückt dann alle \nzu nur einem zusammen. Also, es funktioniert, nicht sicher, was damit zu tun ist, dass dies auf Windows und nicht auf UNIX zutrifft.