Wie kann ich eine neue Zeile (\ n) mit sed ersetzen?


1371

Wie kann ich mit dem Befehl eine neue Zeile (" \n") durch ein Leerzeichen (" ") ersetzen sed?

Ich habe erfolglos versucht:

sed 's#\n# #g' file
sed 's#^$# #g' file

Wie behebe ich das?


27
trist nur dann das richtige Werkzeug für den Job, wenn ein einzelnes Zeichen durch ein einzelnes Zeichen ersetzt wird, während das obige Beispiel zeigt, wie Zeilenumbrüche durch Leerzeichen ersetzt werden. Im obigen Beispiel könnte tr funktionieren. Dies würde jedoch später einschränken.
Wütend 84

9
trim richtigen Werkzeug für den Job, weil der Fragesteller jede neue Zeile durch ein Leerzeichen ersetzen wollte, wie in seinem Beispiel gezeigt. Das Ersetzen von Zeilenumbrüchen ist einzigartig geheimnisvoll, sedaber leicht zu erledigen tr. Dies ist eine häufige Frage. Das Durchführen von Regex-Ersetzungen erfolgt nicht durch, trsondern durch sed, was das richtige Werkzeug wäre ... für eine andere Frage.
Mike S

3
"tr" kann auch nur die neue Zeile "tr -d" \ n "löschen. Sie können jedoch auch Rückgaben löschen, um universeller" tr -d "\ 012 \ 015" zu sein.
Anthony

2
WARNUNG: "tr" verhält sich in Bezug auf Zeichenbereiche zwischen Linux und älteren Solaris-Computern (EG sol5.8) unterschiedlich. EG: "tr-d" az "und" tr-d "[az]". Dafür empfehle ich Ihnen "sed" zu verwenden, was diesen Unterschied nicht hat.
Anthony

2
@ MikeS Danke für die Antwort. Folgen Sie tr '\012' ' 'mit einem echo. Andernfalls wird auch der letzte Zeilenvorschub in der Datei gelöscht. tr '\012' ' ' < filename; echomacht den Trick.
Bernie Reiter

Antworten:


1513

Verwenden Sie diese Lösung mit GNU sed:

sed ':a;N;$!ba;s/\n/ /g' file

Dadurch wird die gesamte Datei in einer Schleife gelesen und die neue (n) Zeile (n) durch ein Leerzeichen ersetzt.

Erläuterung:

  1. Erstellen Sie ein Etikett über :a.
  2. Hängen Sie die aktuelle und die nächste Zeile über an den Musterbereich an N.
  3. Wenn wir uns vor der letzten Zeile befinden, verzweigen Sie zum erstellten Label $!ba( $!bedeutet, dass Sie dies nicht in der letzten Zeile tun, da es eine letzte neue Zeile geben sollte).
  4. Schließlich ersetzt die Ersetzung jede neue Zeile durch ein Leerzeichen im Musterbereich (der die gesamte Datei darstellt).

Hier ist eine plattformübergreifende kompatible Syntax, die mit BSD und OS X funktioniert sed(gemäß @ Benjie-Kommentar ):

sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' file

Wie Sie sehen können, ist die Verwendung sedfür dieses ansonsten einfache Problem problematisch. Eine einfachere und adäquate Lösung finden Sie in dieser Antwort .


45
@Arjan und Masi: OS X verwendet BSDs sedanstelle von GNU sed, daher kann es einige subtile (und einige nicht so subtile) Unterschiede zwischen den beiden geben. Dies ist ein ständiger Schmerz, wenn Sie sowohl auf OS X- als auch auf * nix-Computern arbeiten. Normalerweise installiere ich GNUs coreutilsund findutilsunter OS X und ignoriere die BSD-Versionen.
Telemachos

50
Das :aist kein Register, es ist ein Zweigstellenlabel. Es ist ein Ziel für den bBefehl *, der wie "goto" funktioniert. Wenn Sie es als Register bezeichnen, können Sie Speicherorte erstellen. Es gibt nur zwei "Register"; Einer wird als "Haltebereich" bezeichnet, den Ihr Skript nicht verwendet, und der andere wird als "Musterbereich" bezeichnet. Der NBefehl hängt eine neue Zeile und die nächste Zeile der Eingabedatei an den Musterbereich an. [* Sie können mehrere Beschriftungen und bBefehle haben. Wenn Sie einen bBefehl ohne angehängtes Label char haben, verzweigt er sich zum Ende des Skripts, um die nächste Zeile zu lesen und die Schleife erneut
durchzuführen

108
Sie können diese plattformübergreifende Ausführung (dh unter Mac OS X) ausführen, indem Sie die Befehle separat ausführen, anstatt sie durch Semikolons zu trennen: sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g'
Benjie

74
Warum kommentiert niemand, was für ein dummes Durcheinander das ist (nicht die Antwort selbst, sondern das Programm, für das die vorgeschlagene Antwort die beste Lösung für ein sehr einfaches Problem ist). Sed sieht aus wie ein Auto, das normalerweise gut fährt. Wenn Sie jedoch zu einer bestimmten Straße in der Nähe fahren möchten, können Sie das Auto nur mit einem Hubschrauber anheben.
Ark-Kun

12
Komm schon Leute - 261 Gegenstimmen für eine verrückte, unverständliche Lösung, die nicht funktioniert ???? sed ist ein hervorragendes Werkzeug für einfache Unterteilungen in einer einzelnen Zeile, für alles andere verwenden Sie einfach awk. Gute Trauer ....
Ed Morton

1711

sedist für die zeilenbasierte Eingabe vorgesehen. Obwohl es tun kann, was Sie brauchen.


Eine bessere Option ist, den trBefehl wie folgt zu verwenden:

tr '\n' ' ' < input_filename

oder entfernen Sie die Zeilenumbruchzeichen vollständig:

tr -d '\n' < input.txt > output.txt

oder wenn Sie die GNU-Version haben (mit ihren langen Optionen)

tr --delete '\n' < input.txt > output.txt

88
Sed ist linienbasiert, daher ist es schwierig, Zeilenumbrüche zu erfassen.
Alexander Gladysh

191
sed arbeitet mit einem "Strom" von Eingaben, versteht es jedoch in durch Zeilenumbrüche getrennten Abschnitten. Es ist ein Unix-Tool, was bedeutet, dass es eine Sache sehr gut macht. Die eine Sache ist "zeilenweise an einer Datei arbeiten". Es wird schwierig sein, etwas anderes zu tun, und es besteht die Gefahr, dass es fehlerhaft ist. Die Moral der Geschichte lautet: Wählen Sie das richtige Werkzeug. Viele Ihrer Fragen scheinen die Form zu haben: "Wie kann ich dieses Tool dazu bringen, etwas zu tun, für das es nie gedacht war?" Diese Fragen sind interessant, aber wenn sie im Zuge der Lösung eines echten Problems auftauchen, machen Sie es wahrscheinlich falsch.
dmckee --- Ex-Moderator Kätzchen

7
@JBBrown trist ein oft übersehenes Juwel für den Bau von Pipelines.
dmckee --- Ex-Moderator Kätzchen

70
tr ist großartig, aber Sie können Zeilenumbrüche nur durch einzelne Zeichen ersetzen. Sie müssen ein anderes Werkzeug verwenden, wenn Sie Zeilenumbrüche durch eine Zeichenfolge ersetzen möchten
Eddy

21
@Eddy - Ich habe tr verwendet, um neue Zeilen durch ein Zeichen zu ersetzen, das nicht im Text enthalten war (ich habe Backtick verwendet), und dann sed, um den Backtick durch die Zeichenfolge zu ersetzen, die ich verwenden wollte
rjohnston

494

Schnelle Antwort

sed ':a;N;$!ba;s/\n/ /g' file
  1. : a Erstelle ein Label 'a'
  2. N Fügen Sie die nächste Zeile an den Musterbereich an
  3. $! wenn nicht die letzte Zeile , ba Zweig (go to) Label 'a'
  4. s Ersatz , / \ n / Regex für neue Zeile , / / durch ein Leerzeichen , / g globale Übereinstimmung (so oft es geht)

sed durchläuft die Schritte 1 bis 3, bis die letzte Zeile erreicht ist, wobei alle Zeilen in den Musterbereich passen, in dem sed alle \ n Zeichen ersetzt


Alternativen

Im Gegensatz zu sed müssen alle Alternativen nicht die letzte Zeile erreichen, um den Prozess zu starten

mit Bash , langsam

while read line; do printf "%s" "$line "; done < file

mit perl , sed- ähnlicher Geschwindigkeit

perl -p -e 's/\n/ /' file

mit tr , schneller als sed , kann nur durch ein Zeichen ersetzt werden

tr '\n' ' ' < file

mit Einfügen , tr- ähnliche Geschwindigkeit, kann nur durch ein Zeichen ersetzt werden

paste -s -d ' ' file

mit awk , tr- ähnlicher Geschwindigkeit

awk 1 ORS=' ' file

Eine andere Alternative wie "echo $ (<Datei)" ist langsam, funktioniert nur bei kleinen Dateien und muss die gesamte Datei verarbeiten, um den Prozess zu starten.


Lange Antwort aus den sed FAQ 5.10

5.10. Warum kann ich eine neue Zeile nicht mit der Escape-
Sequenz \ n abgleichen oder löschen ? Warum kann ich mit \ n nicht zwei oder mehr Zeilen abgleichen?

Das \ n wird niemals mit der neuen Zeile am
Zeilenende übereinstimmen , da die neue Zeile immer entfernt wird, bevor die Zeile in den
Musterbereich eingefügt wird . Verwenden Sie
den Befehl 'N' oder ähnliches (z. B. 'H; ...; g;'), um zwei oder mehr Zeilen in den Musterraum zu bringen .

Sed funktioniert folgendermaßen: sed liest jeweils eine Zeile,
schneidet die abschließende neue Zeile ab , fügt die verbleibenden Elemente in den Musterbereich ein, in dem
das sed-Skript sie adressieren oder ändern kann, und
hängt beim Drucken des Musterbereichs eine neue Zeile an stdout an (oder zu einer Datei). Wenn der
Musterbereich mit 'd' oder 'D' ganz oder teilweise gelöscht wird,
wird in solchen Fällen die neue Zeile nicht hinzugefügt. So mögen Skripte

  sed 's/\n//' file       # to delete newlines from each line             
  sed 's/\n/foo\n/' file  # to add a word to the end of each line         

funktioniert NIEMALS, da die nachfolgende neue Zeile entfernt wird, bevor
die Zeile in den Musterbereich eingefügt wird .
Verwenden Sie stattdessen eines der folgenden Skripte, um die oben genannten Aufgaben auszuführen :

  tr -d '\n' < file              # use tr to delete newlines              
  sed ':a;N;$!ba;s/\n//g' file   # GNU sed to delete newlines             
  sed 's/$/ foo/' file           # add "foo" to end of each line          

Da andere Versionen von sed als GNU sed die Größe des Musterpuffers einschränken
, ist hier das Unix-Dienstprogramm 'tr' zu bevorzugen.
Wenn die letzte Zeile der Datei eine neue Zeile enthält, fügt GNU sed
diese neue Zeile zur Ausgabe hinzu, löscht jedoch alle anderen, während tr
alle neuen Zeilen löscht.

Um einen Block mit zwei oder mehr Zeilen abzugleichen, gibt es drei grundlegende Möglichkeiten:
(1) Verwenden Sie den Befehl 'N', um die nächste Zeile zum Musterbereich hinzuzufügen.
(2) Verwenden Sie den Befehl 'H' mindestens zweimal, um die aktuelle Zeile
an den Haltebereich anzuhängen , und rufen Sie dann die Zeilen
mit x, g oder G aus dem Haltebereich ab . oder (3) Adressbereiche verwenden (siehe Abschnitt 3.3 oben)
, um Zeilen zwischen zwei angegebenen Adressen abzugleichen.

Durch Auswahl von (1) und (2) wird ein \ n in den Musterraum
eingefügt , wo es wie gewünscht adressiert werden kann ('s / ABC \ nXYZ / alphabet / g'). Ein Beispiel
für die Verwendung von 'N' zum Löschen eines Zeilenblocks finden Sie in Abschnitt 4.13
("Wie lösche ich einen Block bestimmter aufeinanderfolgender Zeilen?"). Dieses
Beispiel kann geändert werden, indem der Löschbefehl in etwas
anderes geändert wird , z. B. 'p' (Drucken), 'i' (Einfügen), 'c' (Ändern), 'a' (Anhängen)
oder 's' (Ersetzen). .

Mit Auswahl (3) wird kein \ n in den Musterbereich eingefügt, es stimmt jedoch
mit einem Block aufeinanderfolgender Zeilen überein. Daher
benötigen Sie möglicherweise nicht einmal das \ n, um das Gesuchte zu finden. Seit GNU sed unterstützt
Version 3.02.80 diese Syntax:

  sed '/start/,+4d'  # to delete "start" plus the next 4 lines,           

Zusätzlich zu den herkömmlichen Bereichsadressen '/ from here /, / to there / {...}'
kann die Verwendung von \ n möglicherweise vollständig vermieden werden.


6
trwar eine großartige Idee, und Ihre Gesamtabdeckung sorgt für eine qualitativ hochwertige Antwort.
New Alexandria

1
+1 für die Verwendung ( Standarddienstprogramm ) paste... und alle anderen!
Totor

1
@elgalu versuchen Sie diese unix.stackexchange.com/questions/4527/…
hdorio

4
Das Beste an dieser Antwort ist, dass die "lange Antwort" genau erklärt, wie und warum der Befehl funktioniert.
pdwalker

3
Dies ist möglicherweise die hilfreichste der Tausenden von Antworten, die ich bei Stackexchange gelesen habe. Ich muss mehrere Zeichen über Zeilen hinweg abgleichen. Keine früheren sed-Beispiele deckten mehrere Zeilen ab, und tr kann keine Mehrfachzeichenübereinstimmung verarbeiten. Perl sieht gut aus, funktioniert aber nicht wie erwartet. Ich würde diese Antwort mehrmals abstimmen, wenn ich könnte.
Mightypile

225

Eine kürzere awk Alternative:

awk 1 ORS=' '

Erläuterung

Ein awk-Programm besteht aus Regeln, die aus bedingten Codeblöcken bestehen, dh:

condition { code-block }

Wenn der Codeblock weggelassen wird, wird die Standardeinstellung verwendet : { print $0 }. Somit wird das 1als eine wahre Bedingung interpretiert und print $0für jede Zeile ausgeführt.

Wenn awkdie Eingabe gelesen wird, wird sie basierend auf dem Wert von RS(Datensatztrennzeichen), der standardmäßig eine neue Zeile ist, in Datensätze aufgeteilt. Daher awkwird die Eingabe standardmäßig zeilenweise analysiert. Bei der Aufteilung wird auch RSder Eingabedatensatz entfernt.

Wenn nun ein Datensatz gedruckt wird ORS(Output Record Separator), wird standardmäßig wieder eine neue Zeile eingefügt. ORSWenn Sie also zu einem Leerzeichen wechseln , werden alle Zeilenumbrüche in Leerzeichen geändert.


5
Ich mag diese einfache Lösung, die viel besser lesbar ist, sehr als andere
Fedir RYKHTIK

8
Wenn es sinnvoller ist, könnte dies effektiv geschrieben werden als: awk 'BEGIN { ORS=" " } { print $0 } END { print "\n"} ' file.txt(Hinzufügen einer neuen Endzeile, um Anfang / Ende zu veranschaulichen); Die "1" ergibt true(Zeile verarbeiten) und print(Zeile drucken). Zu diesem Ausdruck könnte auch eine Bedingung hinzugefügt werden, z. B. nur die Bearbeitung von Linien, die einem Muster entsprechen: awk 'BEGIN { ORS=" " } /pattern/ { print $0 } END { print "\n"} '
Michael

2
Sie können es einfacher machen: codeawk 'ORS = ""' file.txtcode
Udi

Wenn Sie awk wie dieses verwenden, wird leider auch der letzte Zeilenvorschub in der Datei gelöscht. Siehe Patrick Dark Antwort oben über die Verwendung von 'tr' in einer Unterschale wie 'cat file | echo $ (tr "\ 012" "") `was den Trick macht. Raffiniert.
Bernie Reiter

143

gnu sed hat eine Option -zfür null getrennte Datensätze (Zeilen). Sie können einfach anrufen:

sed -z 's/\n/ /g'

4
Selbst wenn die Eingabe Nullen enthält, bleiben diese erhalten (als Datensatzbegrenzer).
Toby Speight

6
Wird dadurch nicht die gesamte Eingabe geladen, wenn keine Nullen vorhanden sind? In diesem Fall kann die Verarbeitung einer Multi-Gigabyte-Datei abstürzen.
Ruslan

3
@ Ruslan, ja, es lädt die gesamte Eingabe. Diese Lösung ist keine gute Idee für Multi-Gigabyte-Dateien.
JJoao

7
Dies ist im Ernst die beste Antwort. Die anderen Ausdrücke sind zu verzerrt, um sich zu erinnern. @JJoao Sie können es mit verwenden -u, --unbuffered. Der manMagier sagt: "Laden Sie minimale Datenmengen aus den Eingabedateien und leeren Sie die Ausgabepuffer häufiger."
not2qubit

damit. viel. diese.
Sjas

85

Die Perl- Version funktioniert wie erwartet.

perl -i -p -e 's/\n//' file

Wie in den Kommentaren erwähnt, ist es erwähnenswert, dass diese Änderungen vorgenommen wurden. -i.bakSie erhalten vor dem Ersetzen eine Sicherungskopie der Originaldatei, falls Ihr regulärer Ausdruck nicht so intelligent ist, wie Sie gedacht haben.


23
Bitte erwähnen Sie zumindest, dass -iohne Suffix keine Sicherung erfolgt . -i.bakschützt Sie vor einem einfachen, hässlichen Fehler (z. B. vergessen -p, die Datei einzugeben und auf Null zu setzen).
Telemachos

6
@ Telemachus: Es ist ein fairer Punkt, aber es kann so oder so argumentiert werden. Der Hauptgrund, warum ich es nicht erwähnt habe, ist, dass das sed-Beispiel in der OP-Frage keine Backups erstellt, sodass es hier überflüssig erscheint. Der andere Grund ist, dass ich die Sicherungsfunktion noch nie verwendet habe (ich finde automatische Sicherungen eigentlich ärgerlich), daher vergesse ich immer, dass sie vorhanden ist. Der dritte Grund ist, dass meine Befehlszeile um vier Zeichen länger wird. Zum Guten oder Schlechten (wahrscheinlich zum Schlechten) bin ich ein zwanghafter Minimalist; Ich bevorzuge nur die Kürze. Mir ist klar, dass Sie nicht einverstanden sind. Ich werde mein Bestes geben, um mich daran zu erinnern, in Zukunft vor Backups zu warnen.
ire_and_curses

6
@Ire_and_curses: Eigentlich hast du gerade ein verdammt gutes Argument dafür gemacht, mich zu ignorieren. Das heißt, Sie haben Gründe für Ihre Entscheidungen, und ob ich mit den Entscheidungen einverstanden bin oder nicht, ich respektiere das mit Sicherheit. Ich bin mir nicht ganz sicher, warum, aber ich habe in letzter Zeit eine Träne über diese bestimmte Sache (die -iFlagge in Perl ohne Suffix). Ich bin sicher, ich werde bald etwas anderes finden, von dem ich besessen sein kann. :)
Telemachos

Es ist wirklich bedauerlich, dass dies mit stdin nicht funktioniert, wenn Sie den -Dateinamen angeben. Gibt es eine Möglichkeit, das zu tun? Das ist meine Vorgehensweise, um mir keine Sorgen über das Ändern einer Datei zu machen, indem ich eine Pipeline verwende, die mit cat beginnt.
Steven Lu

@StevenLu Perl liest standardmäßig aus STDIN, wenn keine Dateinamen angegeben sind. So könnten Sie zBperl -i -p -e 's/\n//' < infile > outfile
ire_and_curses

44

Wer braucht sed? Hier ist der bashWeg:

cat test.txt |  while read line; do echo -n "$line "; done

2
Upvote, ich habe normalerweise die Top-Antwort verwendet, aber wenn ich / dev / urandom durchpfeife, wird sed erst mit EOF gedruckt, und ^ C ist kein EOF. Diese Lösung wird jedes Mal gedruckt, wenn eine neue Zeile angezeigt wird. Genau das, was ich brauchte! Vielen Dank!
Vasiliy Sharapov

1
dann warum nicht: echo -n `cat days.txt` Von diesem Beitrag
Tony

9
@ Tony, weil Backticks veraltet sind und die Katze überflüssig ist ;-) Verwendung: echo $ (<days.txt)
seumasmac

10
Ohne auch nur zu verwenden cat: while read line; do echo -n "$line "; done < test.txt. Kann nützlich sein, wenn eine Sub-Shell ein Problem darstellt.
Carlo Cannas

5
echo $(<file)drückt alle Leerzeichen auf ein einziges Leerzeichen, nicht nur Zeilenumbrüche: Dies geht über das hinaus, was das OP verlangt.
Glenn Jackman

27

So ersetzen Sie alle Zeilenumbrüche mit awk durch Leerzeichen, ohne die gesamte Datei in den Speicher einzulesen:

awk '{printf "%s ", $0}' inputfile

Wenn Sie einen endgültigen Zeilenumbruch wünschen:

awk '{printf "%s ", $0} END {printf "\n"}' inputfile

Sie können ein anderes Zeichen als Leerzeichen verwenden:

awk '{printf "%s|", $0} END {printf "\n"}' inputfile

END{ print ""}ist eine kürzere Alternative für eine nachfolgende Newline.
Isaac

22
tr '\n' ' ' 

ist der Befehl.

Einfach und leicht zu bedienen.


14
oder einfach, tr -d '\n'wenn Sie kein Leerzeichen hinzufügen möchten
Spuder

21

Drei Dinge.

  1. tr(oder catusw.) wird absolut nicht benötigt. (GNU) sedund (GNU) awkkönnen in Kombination 99,9% aller benötigten Textverarbeitungen ausführen.

  2. stream! = zeilenbasiert. edist ein zeilenbasierter Editor. sedist nicht. Siehe sed Vortrag , um weitere Informationen über den Unterschied. Die meisten Leute verwechseln seddie Zeilenorientierung , da die Musterübereinstimmung für EINFACHE Übereinstimmungen standardmäßig nicht sehr gierig ist. Wenn Sie beispielsweise Muster suchen und durch ein oder zwei Zeichen ersetzen, wird sie standardmäßig nur bei der ersten Übereinstimmung ersetzt es findet (sofern im globalen Befehl nicht anders angegeben). Es würde nicht einmal einen globalen Befehl geben, wenn er eher zeilenbasiert als STREAM-basiert wäre, da nur Zeilen gleichzeitig ausgewertet würden. Versuche zu rennen ed; Sie werden den Unterschied bemerken. edist ziemlich nützlich, wenn Sie über bestimmte Zeilen iterieren möchten (z. B. in einer for-Schleife), aber meistens nur sed.

  3. Davon abgesehen,

    sed -e '{:q;N;s/\n/ /g;t q}' file
    

    funktioniert gut in GNU sedVersion 4.2.1. Der obige Befehl ersetzt alle Zeilenumbrüche durch Leerzeichen. Es ist hässlich und etwas umständlich einzutippen, aber es funktioniert gut. Die {}können weggelassen werden, da sie nur aus Gründen der Vernunft enthalten sind.


3
Als eine Person, die nur genug weiß sed, um grundlegende Dinge zu tun, muss ich sagen, dass es mehr als nur darum geht, was man damit machen kann , sedsondern vielmehr darum , wie einfach es ist, zu verstehen, was los ist. Es fällt mir sehr schwer, damit zu arbeiten, seddaher würde ich einen einfacheren Befehl bevorzugen, wenn ich ihn verwenden kann.
Nate

Unter Verwendung t qals bedingter Sprung dies funktioniert mit einem Muster wie s/\n / /(alle Linien zu verbinden , die mit einem Leerzeichen beginnen) , ohne die gesamte Datei in dem Speicher zu lesen. Praktisch beim Transformieren von Multi-Megabyte-Dateien.
Textshell

Der Artikel, den Sie verlinkt haben, spiegelt nicht wider, was Sie sagen
hek2mgl

Dies ist fast 800-mal langsamer als die akzeptierte Antwort bei großen Eingaben. Dies ist darauf zurückzuführen, dass bei immer größeren Eingaben für jede Zeile ein Ersatz ausgeführt wird.
Thor

13

Die Antwort mit dem: ein Etikett ...

Wie kann ich eine neue Zeile (\ n) mit sed ersetzen?

... funktioniert nicht in freebsd 7.2 in der Kommandozeile:

(echo foo; echo bar) | sed ': a; N; $! ba; s / \ n / / g'
sed: 1: ": a; N; $! ba; s / \ n / / g": nicht verwendetes Label 'a; N; $! ba; s / \ n / / g'
foo
Bar

Aber funktioniert es, wenn Sie das sed-Skript in eine Datei einfügen oder -e verwenden, um das sed-Skript zu "erstellen" ...

> (echo foo; echo bar) | sed -e: a -e N -e '$! ba' -e 's / \ n / / g'
Foo Bar

oder ...

> cat > x.sed << eof
:a
N
$!ba
s/\n/ /g
eof

> (echo foo; echo bar) | sed -f x.sed
foo bar

Vielleicht ist das sed in OS X ähnlich.


Die Reihe von -e Argumenten funktionierte für mich unter Windows mit MKS! Vielen Dank!
JamesG

12

Leicht verständliche Lösung

Ich hatte dieses Problem. Der Kicker war, dass ich die Lösung brauchte, um an BSDs (Mac OS X) und GNUs (Linux und Cygwin ) zu arbeiten sedund tr:

$ echo 'foo
bar
baz


foo2
bar2
baz2' \
| tr '\n' '\000' \
| sed 's:\x00\x00.*:\n:g' \
| tr '\000' '\n'

Ausgabe:

foo
bar
baz

(hat nachgestellte Newline)

Es funktioniert unter Linux, OS X und BSD - auch ohne UTF-8- Unterstützung oder mit einem beschissenen Terminal.

  1. Verwenden Sie trdiese Option, um die neue Zeile mit einem anderen Zeichen zu tauschen.

    NULL( \000oder \x00) ist nett, weil es keine UTF-8-Unterstützung benötigt und wahrscheinlich nicht verwendet wird.

  2. Verwenden Sie sed, um die zu entsprechenNULL

  3. Verwenden trSie diese Option, um zusätzliche Zeilenumbrüche bei Bedarf zurückzutauschen


1
Ein subtiler Hinweis zur Nomenklatur: Das Zeichen \000wird allgemein als NUL(ein L) bezeichnet und NULLwird im Allgemeinen verwendet, wenn von einem Nullzeiger gesprochen wird (in C / C ++).
Woche


9

Ich bin kein Experte, aber ich denke, sedSie müssten zuerst die nächste Zeile in den Musterraum einfügen, indem Sie " N" verwenden. Aus dem Abschnitt "Multiline Pattern Space" in "Advanced sed Commands" des Buches sed & awk (Dale Dougherty und Arnold Robbins; O'Reilly 1997; Seite 107 in der Vorschau ):

Der mehrzeilige Next (N) -Befehl erstellt einen mehrzeiligen Musterraum, indem eine neue Eingabezeile gelesen und an den Inhalt des Musterraums angehängt wird. Der ursprüngliche Inhalt des Musterbereichs und die neue Eingabezeile werden durch eine neue Zeile getrennt. Das eingebettete Zeilenumbruchzeichen kann durch die Escape-Sequenz "\ n" in Mustern abgeglichen werden. In einem mehrzeiligen Musterraum entspricht das Metazeichen "^" dem allerersten Zeichen des Musterraums und nicht den Zeichen, die auf eingebettete Zeilenumbrüche folgen. In ähnlicher Weise stimmt "$" nur mit der letzten neuen Zeile im Musterbereich überein und nicht mit eingebetteten neuen Zeilen. Nachdem der Befehl Weiter ausgeführt wurde, wird die Steuerung an nachfolgende Befehle im Skript übergeben.

Von man sed:

[2addr] N.

Fügen Sie die nächste Eingabezeile mit einem eingebetteten Zeilenumbruchzeichen an den Musterbereich an, um das angehängte Material vom ursprünglichen Inhalt zu trennen. Beachten Sie, dass sich die aktuelle Zeilennummer ändert.

Ich habe dies verwendet , um (mehrere) schlecht formatierte Protokolldateien zu durchsuchen, in denen die Suchzeichenfolge möglicherweise in einer "verwaisten" nächsten Zeile gefunden wird.


7

Ich habe einen hybriden Ansatz verwendet, um die Newline-Sache zu umgehen, indem ich tr verwendet habe, um Newlines durch Tabs zu ersetzen, und dann Tabs durch alles ersetzt habe, was ich will. In diesem Fall "
", da ich versuche, HTML-Unterbrechungen zu generieren.

echo -e "a\nb\nc\n" |tr '\n' '\t' | sed 's/\t/ <br> /g'`

6

Als Antwort auf die obige "tr" -Lösung unter Windows (wahrscheinlich unter Verwendung der Gnuwin32-Version von tr) wurde die vorgeschlagene Lösung vorgeschlagen:

tr '\n' ' ' < input

funktionierte nicht für mich, es würde entweder einen Fehler machen oder das \ nw / '' aus irgendeinem Grund ersetzen.

Mit einer anderen Funktion von tr funktionierte die Option "Löschen" -d jedoch:

tr -d '\n' < input

oder '\ r \ n' anstelle von '\ n'


3
Unter Windows müssen Sie wahrscheinlich verwenden tr "\n" " " < input. Die Windows-Shell (cmd.exe) behandelt den Apostroph nicht als Anführungszeichen.
Keith Thompson

Nein, im Windows 10 Ubuntu-Subsystem müssen Sietr "\n\r" " " < input.txt > output.txt
user1491819

Dies funktioniert unter Windows 10 mit Gnuwin32 : cat SourceFile.txt | tr --delete '\r\n' > OutputFile.txt. Oder verwenden Sie anstelle von Gnuwin32 Gow (Gnu unter Windows), github.com/bmatzelle/gow/wiki
Alchemistmatt

5

Kugelsichere Lösung. Binärdatensicher und POSIX-kompatibel, aber langsam.

POSIX sed erfordert eine Eingabe gemäß der POSIX-Textdatei und den POSIX- Zeilendefinitionen, sodass NULL-Bytes und zu lange Zeilen nicht zulässig sind und jede Zeile mit einer neuen Zeile (einschließlich der letzten Zeile) enden muss. Dies macht es schwierig, sed für die Verarbeitung beliebiger Eingabedaten zu verwenden.

Die folgende Lösung vermeidet sed und konvertiert stattdessen die eingegebenen Bytes in Oktalcodes und dann wieder in Bytes, fängt jedoch den Oktalcode 012 (Zeilenumbruch) ab und gibt stattdessen die Ersatzzeichenfolge aus. Soweit ich das beurteilen kann, ist die Lösung POSIX-kompatibel und sollte daher auf einer Vielzahl von Plattformen funktionieren.

od -A n -t o1 -v | tr ' \t' '\n\n' | grep . |
  while read x; do [ "0$x" -eq 012 ] && printf '<br>\n' || printf "\\$x"; done

POSIX-Referenzdokumentation: sh , Shell-Befehlssprache , od , tr , grep , read , [ , printf .

Beide read, [und printfsind zumindest in Bash integriert, aber das wird von POSIX wahrscheinlich nicht garantiert. Auf einigen Plattformen kann es daher sein, dass jedes Eingabebyte einen oder mehrere neue Prozesse startet, was die Arbeit verlangsamt. Selbst in Bash erreicht diese Lösung nur etwa 50 kB / s, daher ist sie nicht für große Dateien geeignet.

Getestet unter Ubuntu (Bash, Dash und Busybox), FreeBSD und OpenBSD.


5

In einigen Situationen können Sie möglicherweise RSzu einer anderen Zeichenfolge oder einem anderen Zeichen wechseln . Auf diese Weise ist \ n für sub / gsub verfügbar:

$ gawk 'BEGIN {RS="dn" } {gsub("\n"," ") ;print $0 }' file

Die Stärke von Shell-Skripten besteht darin, dass Sie es auf andere Weise tun können, wenn Sie nicht wissen, wie es auf eine Weise zu tun ist. Und oft müssen Sie mehr Dinge berücksichtigen, als eine komplexe Lösung für ein einfaches Problem zu finden.

In Bezug auf die Sache, dass gawk langsam ist ... und die Datei in den Speicher liest, weiß ich das nicht, aber für mich scheint gawk mit jeweils einer Zeile zu arbeiten und ist sehr sehr schnell (nicht so schnell wie einige der anderen , aber auch die Zeit zum Schreiben und Testen zählt).

Ich verarbeite MB und sogar GB Daten, und die einzige Grenze, die ich gefunden habe, ist die Zeilengröße.


5

Wenn Sie das Pech haben, sich mit Windows-Zeilenenden befassen zu müssen, müssen Sie das \rund das entfernen\n

tr '[\r\n]' ' ' < $input > $output

Dies wird [durch einen Raum und \rdurch einen Raum und \ndurch einen Raum und ]durch einen Raum ersetzt. tr -d '\r\n' <filewürde alle \roder \nZeichen entfernen , aber das ist auch nicht das, was gefragt wird. tr -d '\r' <fileentfernt alle \rZeichen (unabhängig davon, ob sie benachbart sind \n), was wahrscheinlich eher nützlich als möglicherweise für die Anforderungen des OP korrekt ist (vorausgesetzt, Sie trverstehen diese Backslash-Notation).
Tripleee

4

Sie können verwenden xargs- es wird \nstandardmäßig durch ein Leerzeichen ersetzt.

Es würde jedoch Probleme geben, wenn Ihre Eingabe einen Fall von a aufweist unterminated quote, z. B. wenn die Anführungszeichen in einer bestimmten Zeile nicht übereinstimmen.


Xargs behandelt auch die letzte Zeile gut:
AAAfarmclub

4

Findet und ersetzt mit allow \ n

sed -ie -z 's/Marker\n/# Marker Comment\nMarker\n/g' myfile.txt

Marker

Wird

# Markerkommentar

Marker


4

Warum habe ich mit keine einfache Lösung gefunden awk?

awk '{printf $0}' file

printf druckt jede Zeile ohne Zeilenumbrüche, wenn Sie die ursprünglichen Zeilen durch ein Leerzeichen oder ein anderes trennen möchten:

awk '{printf $0 " "}' file

echo "1\n2\n3" | awk '{printf $0}', das funktioniert bei mir. @ edi9999
Itachi

Es tut dir leid, ich habe das fin printf
edi9999

Dies war der einzige Ansatz, der für mich innerhalb von Git Bash für Windows funktioniert hat
Plato

3

Unter Mac OS X (mit FreeBSD sed):

# replace each newline with a space
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g; ta'
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g' -e ta


3

Awk verwenden:

awk "BEGIN { o=\"\" }  { o=o \" \" \$0 }  END { print o; }"

2
Sie müssen die Anführungszeichen und das Dollarzeichen nicht umgehen, wenn Sie die äußeren in einfache Anführungszeichen ändern. Der Buchstabe "o" wird normalerweise als schlechte Wahl als Variablenname angesehen, da er mit der Ziffer "0" verwechselt werden kann. Sie müssen Ihre Variable auch nicht initialisieren, sie ist standardmäßig eine Nullzeichenfolge. Wenn Sie jedoch keinen fremden führenden Raum wünschen : awk '{s = s sp $0; sp = " "} END {print s}'. In meiner Antwort finden Sie jedoch eine Möglichkeit, awk zu verwenden, ohne die gesamte Datei in den Speicher einzulesen.
Bis auf weiteres angehalten.

Bitte überprüfen Sie stattdessen Thors Antwort . Es ist viel effizienter, lesbarer und auf jeden Fall besser , diesen Ansatz zu vergleichen (obwohl dies funktionieren würde )!
Mschilli

Alter, ich verstehe. Keine Notwendigkeit, es mir ins Gesicht zu reiben :-) Thors Antwort ist sowieso weit oben auf der Seite (was richtig ist), also was kümmert es dich?
Kralyk

3

Eine Lösung, die mir besonders gefällt, besteht darin, die gesamte Datei im Haltebereich anzuhängen und alle Zeilenumbrüche am Ende der Datei zu ersetzen:

$ (echo foo; echo bar) | sed -n 'H;${x;s/\n//g;p;}'
foobar

Jemand sagte mir jedoch, dass der Haltebereich in einigen sed-Implementierungen begrenzt sein kann.


1
Die Ersetzung durch eine leere Zeichenfolge in Ihrer Antwort verbirgt die Tatsache, dass die Verwendung von H zum Anhängen an den Haltebereich bedeutet, dass der Haltebereich mit einer neuen Zeile beginnt. Um dies zu vermeiden, müssen Sie1h;2,$H;${x;s/\n/x/g;p}
Jeff

3

Ersetzen Sie Zeilenumbrüche durch eine beliebige Zeichenfolge und ersetzen Sie auch die letzte Zeilenumbruch

Die reinen trLösungen können nur durch ein einzelnes Zeichen ersetzt werden, und die reinen sedLösungen ersetzen nicht die letzte neue Zeile der Eingabe. Die folgende Lösung behebt diese Probleme und scheint für Binärdaten sicher zu sein (auch mit einem UTF-8-Gebietsschema):

printf '1\n2\n3\n' |
  sed 's/%/%p/g;s/@/%a/g' | tr '\n' @ | sed 's/@/<br>/g;s/%a/@/g;s/%p/%/g'

Ergebnis:

1<br>2<br>3<br>

Dies ist schlecht, weil es unerwünschte Ausgabe auf jeder Eingabe erzeugt, die enthält@
Steven Lu

@StevenLu: Nein, @in der Eingabe ist OK. Es wird hin %aund zurück entkommen . Die Lösung ist jedoch möglicherweise nicht vollständig POSIX-kompatibel (NULL-Bytes sind nicht zulässig, daher nicht gut für Binärdaten, und alle Zeilen müssen mit newline enden, damit die trAusgabe nicht wirklich gültig ist).
Håkon A. Hjortland

Ah. Ich sehe, du hast es repariert. Ein bisschen verworren für eine einfache Operation, aber gute Arbeit.
Steven Lu

3

Es ist sed , das die neuen Zeilen nach "normaler" Substitution einführt. Zuerst wird das Zeichen für die neue Zeile gekürzt, dann wird es gemäß Ihren Anweisungen verarbeitet, und dann wird eine neue Zeile eingeführt.

Mit sed können Sie "das Ende" einer Zeile (nicht das Zeichen für neue Zeilen) ersetzen, nachdem Sie für jede Eingabezeile eine Zeichenfolge Ihrer Wahl zugeschnitten haben. aber sed ausgeben werden verschiedene Linien. Angenommen, Sie möchten das "Zeilenende" durch "===" ersetzen (allgemeiner als das Ersetzen durch ein einzelnes Leerzeichen):

PROMPT~$ cat <<EOF |sed 's/$/===/g'
first line
second line
3rd line
EOF

first line===
second line===
3rd line===
PROMPT~$

Um das Zeichen für die neue Zeile durch die Zeichenfolge zu ersetzen, können Sie, wie bereits erwähnt, ineffizient tr verwenden , um die Zeichen für die neue Zeile durch ein "Sonderzeichen" zu ersetzen, und dann sed verwenden , um das Sonderzeichen durch die gewünschte Zeichenfolge zu ersetzen .

Zum Beispiel:

PROMPT~$ cat <<EOF | tr '\n' $'\x01'|sed -e 's/\x01/===/g'
first line
second line
3rd line
EOF

first line===second line===3rd line===PROMPT~$

3

Sie können diese Methode auch verwenden

sed 'x;G;1!h;s/\n/ /g;$!d'

Erläuterung

x   - which is used to exchange the data from both space (pattern and hold).
G   - which is used to append the data from hold space to pattern space.
h   - which is used to copy the pattern space to hold space.
1!h - During first line won't copy pattern space to hold space due to \n is
      available in pattern space.
$!d - Clear the pattern space every time before getting next line until the
      last line.

Ablauf:
Wenn die erste Zeile von der Eingabe stammt, wird ein Austausch durchgeführt, sodass 1 in den Speicherbereich geht und \ n in den Musterbereich gelangt. Anschließend wird der Haltebereich an den Musterbereich angehängt. Anschließend wird der Musterbereich ersetzt und gelöscht.
Während des Austauschs der zweiten Zeile wird 2 in den Haltebereich und 1 in den Musterbereich verschoben. Anschließend wird der GHaltebereich in den Musterbereich eingefügt, hdas Muster in diesen kopiert und ersetzt und gelöscht. Dieser Vorgang wird fortgesetzt, bis eof erreicht ist, und dann das genaue Ergebnis gedruckt.


Seien Sie jedoch gewarnt, dass dies echo 'Y' | sed 'x;G;1!h;s/\n/X/g;$!d'zu XY.
Spooky

3

Eine andere GNU- sed Methode, die fast der Antwort von Zsolt Botykai entspricht , jedoch den sedweniger häufig verwendeten y( transliterierten ) Befehl verwendet, mit dem ein Byte Code (das nachfolgende) gespeichert wirdg :

sed ':a;N;$!ba;y/\n/ /'

Man würde hoffen, ydass es schneller läuft als s(vielleicht mit einer trGeschwindigkeit von 20x schneller), aber in GNU ist sed v4.2.2 y ungefähr 4% langsamer als s.


Tragbarere BSD- sed Version:

sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'

2
Mit BSD ist sed yca. 15% schneller. In dieser Antwort finden Sie ein funktionierendes Beispiel.
Thor

Auch mit BSD müssen sed-Befehle nach einem Label beendet werden, also sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'wäre der richtige Weg.
Ghoti
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.