Zeilenumbrüche in Sed unter Mac OS X

44

Ich finde, dass \ndas in sed unter Mac OS X nicht funktioniert. Sagen wir, ich möchte die durch ein einzelnes Leerzeichen getrennten Wörter in Zeilen aufteilen:

# input
foo bar

Ich benutze,

echo "foo bar" | sed 's/ /\n/'

Aber das Ergebnis ist doof, dem \nist nicht entgangen!

foonbar

Nachdem ich mich bei Google erkundigt habe, habe ich eine Problemumgehung gefunden :

echo 'foo bar' | sed -e 's/ /\'$'\n/g'

Nach dem Lesen des Artikels kann ich immer noch nicht verstehen, was es \'$'\n/g'bedeutet. Kann es mir jemand erklären, oder gibt es eine andere Möglichkeit? Vielen Dank!

Ivan ZG Xiao
quelle
Dies würde wahrscheinlich auch funktionieren:echo "foo bar" | tr ' ' '\n'
Glenn Jackman
3
Danke für den Hinweis. Aber momentan verwende ich nur den obigen Fall als Beispiel, ich muss wissen, wie man einem entkommt \n.
Ivan ZG Xiao
Mögliches Duplikat: stackoverflow.com/questions/21621722/…
hyiltiz

Antworten:

27

Sie können brew install gnu-sedund ersetzen Anrufe sedmit gsed.

Wenn Sie nicht die „g“ voranstellen wollen sed, können Sie brew install gnu-sed --with-default-namesund rufen Sie einfach sed.

(Edit: aktualisierte Sudflagge, Hutspitze an Clément.)

Ahmed Fasih
quelle
Die Verwendung der gleichen Software auf allen Plattformen ist (endlich für mich) viel einfacher als der Umgang mit allen Besonderheiten der Mac-Version. Vorsicht, die Option ist jetzt --with-default-namesund nicht --default-names . Da diese Option bei meiner Installation jedoch nicht funktioniert hat, musste ich eine alias gsed=sedin meine Installation einfügen ~/.profile, damit sie funktioniert.
Clément
4
Dies ist eine hässliche Problemumgehung. Es wird nicht erklärt, warum sich sedOS X so verhält, und es wird die falsche Annahme getroffen, die gnu-sedkorrekter ist. Seien Sie kein GNU-Süchtiger und halten Sie sich an POSIX-Standards, um Probleme auf lange Sicht zu vermeiden.
Octosquidopus
So sollte Apple sein Betriebssystem standardmäßig einrichten. Warum den Namen eines Programms verwenden und es dann anders funktionieren lassen? Ich vergeude eine Stunde aus diesem Grund ...
Rascio
@rascio - weil OS X BSD-ähnlich und Linux Linux-ähnlich ist.
iAdjunct
@rascio Ich denke, Sie werden feststellen, dass GNU sed der Newcomer ist; BSD sed stammt aus dem Jahr 1979 (siehe en.wikipedia.org/wiki/Version_7_Unix ).
Duncan Bayne
24

Diese würden auch funktionieren:

echo 'foo bar' | sed 's/ /\
/g'

echo 'foo bar' | sed $'s/ /\\\n/g'

lf=$'\n'; echo 'foo bar' | sed "s/ /\\$lf/g"

Sed von OS X wird \nim Ersetzungsmuster nicht interpretiert , Sie können jedoch einen Literalzeilenvorschub verwenden, dem ein Zeilenfortsetzungszeichen vorangestellt ist. Die Shell wird $'\n'durch einen Literalzeilenvorschub ersetzt, bevor der Befehl sed ausgeführt wird.

Lri
quelle
4
Warum sed $'s/ /\\\n/g'arbeitet, aber nicht sed $'s/\\\n/ /g'?
Alec Jacobson
1
@alec Sie können nicht sedeinmal in Linux / Unix verwenden, um Zeilenumbrüche zu entfernen, da diese bei jedem Zeilenumbruch analysiert / aufgeteilt werden. Wenn Sie dies unter Linux / Unix ausführen, wird es auch nichts echo -e 'foo\nbar' | sed 's/\n//'
bewirken
10

Die gefundene Problemumgehung übergibt eine einzelne Argumentzeichenfolge an sed -e.

Dieses Argument ist eine Zeichenfolge im bekannten sed- s/ / /gFormat.

Diese Zeichenfolge wird in zwei Teilen nacheinander erstellt.

Der erste Teil wird in '...'Form zitiert .

Der zweite Teil ist formell zitiert $'...'.

Der 's/ /\'Teil erhält die einfachen Anführungszeichen entfernt, wird aber ansonsten an sed übergeben, so wie es in der Befehlszeile aussieht. Das heißt, der Backslash wird nicht von Bash gefressen, sondern an Sed weitergereicht.

Das $'\n/g'Teil erhält das Dollarzeichen und die einfachen Anführungszeichen werden entfernt und \nin ein Zeilenumbruchzeichen umgewandelt.

Alles in allem wird das Argument

s / / \ newline/ g

[Das hat Spaß gemacht. Es dauerte eine Weile, um das auszupacken. +1 für eine interessante Frage.]

Spiff
quelle
7

Der Ausdruck $'...'ist ein bash-ism, der ...mit den Standard-Escape-Sequenzen expandiert. Th , \'bevor er bedeutet nur einen Schrägstrich am Ende des zitierten Abschnitt folgt, ist die resultierende Zeichenfolge s/ /\. (Ja, Sie können die Anführungszeichen in der Mitte einer Zeichenfolge ändern; die Zeichenfolge wird dadurch nicht beendet.)

POSIX-Standard sedakzeptiert nur \nals Teil eines Suchmusters. OS X verwendet FreeBSD sed, das ausschließlich POSIX-konform ist. GNU fügt wie üblich zusätzliches Material hinzu, und dann denken alle Linux-Benutzer, dass dies eine Art "Standard" ist (vielleicht wäre ich mehr beeindruckt, wenn einer von ihnen einen Standardprozess hätte).

Geekosaurier
quelle
Jetzt verstehe ich das $'...'Teil. Aber ... was ist s/ /\ ? Was meinst du damit switch quoting?
Ivan ZG Xiao
2
'...'ist eine Art von Shell-Zitaten; $'...'ist ein anderer. Es gibt auch "..."und \xein einzelnes Zeichen zu zitieren. Sie können diese in einem einzigen Wort kombinieren, was dort gemacht wurde, indem Sie von einer normalen ''Zeichenfolge zu einer $''Zeichenfolge wechseln, um die Zeichenfolge zu übersetzen \n. Im Übrigen baut es ein sedKommando auf ( s/text/replacement/flags). In diesem Fall wird der Befehl mit einem Backslash am Ende gestartet, um die angehängte wörtliche Newline zu schützen $'\n/g'. Das Ergebnis ist, dass alle (die /gFlag-) Leerzeichen durch Zeilenumbrüche ersetzt werden.
Geekosaurier
1

Es ist sehr einfach, visuell zu sehen, was passiert. Einfach die Saite wiedergeben!

echo 's/$/\'$'\n/g'

Ergebnisse in

s/$/\
/g

das ist äquivalent zu s/$/\newline/g

Wenn Sie das Extra \vor dem nicht hatten newline, würde die Shell das newlineals das Ende des Befehls vorzeitig interpretieren .

weises Glück
quelle