Auf OSX setzen ab einer bestimmten Zeile einfügen


24

Ich benutze 'sed' schon eine Weile unter Linux, hatte aber einige Schwierigkeiten, es unter OSX zu benutzen, da 'POSIX sed' und 'GNU sed' so viele kleine Unterschiede haben. Derzeit habe ich Probleme damit, nach einer bestimmten Zeilennummer eine Textzeile einzufügen. (in diesem Fall Zeile 4)

Unter Linux würde ich so etwas machen:

sed --in-place "4 a\  mode '0755'" file.txt

Also habe ich es unter OSX versucht:

sed -i "" "4 a\ mode '0755'" file.txt

Dadurch erhalte ich jedoch den Fehler "Zusätzliche Zeichen nach \ am Ende eines Befehls". Irgendwelche Ideen, was hier falsch ist? Habe ich einen Tippfehler? Oder verstehe ich keinen weiteren Unterschied zwischen sed-Versionen?


2
Kann bestätigen, dass dies unter MacOS 10.6.8 nicht funktioniert. Funktioniert gut unter Debian 6.0.3.
Tink

Antworten:


24

Genau genommen erfordert die POSIX-Spezifikation fürsed eine neue Zeile nach a\:

[1addr]a\
text

Schreiben Sie wie zuvor beschrieben Text in die Standardausgabe.

Dies macht Einzeiler ein wenig Schmerzen zu schreiben, die für die folgenden wahrscheinlich der Grund ist GNU - Erweiterung auf die a, iund cBefehle:

Als GNU - Erweiterung, wenn zwischen dem aund der Neuen - Zeile gibt es außer einer whitespace- \Sequenz, dann wird der Text der Zeile, an dem ersten Nicht-Leerzeichen beginnend nach dem a, wie die ersten Zeile des genommen Textblockes. (Dies ermöglicht eine Vereinfachung der Skripterstellung für ein einzeiliges Add.) Diese Erweiterung funktioniert auch mit den Befehlen iund c.

sedUm also mit Ihrer Syntax portabel zu sein , müssen Sie nach dem a\irgendwie eine neue Zeile einfügen . Am einfachsten fügen Sie einfach eine zitierte Zeile ein:

$ sed -e 'a\
> text'

(wo $und >sind Shell-Eingabeaufforderungen). Wenn Sie feststellen, dass ein Schmerz auftritt, hat bash[1] die $' 'Anführungssyntax zum Einfügen von Escape-Zeichen im C-Stil. Verwenden Sie also einfach

sed -e 'a\'$'\n''text'

[1] und mksh (r39b +) und einige Nicht-Bash-Bourne-Shells (zB / bin / sh in FreeBSD 9+)


Sehr informative Antwort. Vielen Dank für den Hinweis zur Bash-Zitat-Syntax.
Cjackson

14

Wie in dieser Antwort beschrieben , ist es hilfreich, wenn Sie sed-Befehle wie iund verwenden a, um mehrere -e "..."Klauseln zu verwenden. Jede dieser Klauseln wird als durch Zeilenumbrüche getrennt verstanden. Die Befehle iund asind in Inline-Sed-Skripten nur schwer zu verwenden (sie sind für die Verwendung in einer mehrzeiligen Sed-Skriptdatei vorgesehen, die mit aufgerufen wird sed -f file ...). Es sieht so aus, als könnten Sie die implizite neue Zeile, die am Ende einer -eKlausel eingefügt wird, nicht verwenden, um die anzufügende Textzeile von der anzuhängenden Textzeile zu trennen a\. Sie können es jedoch verwenden, um die anzufügende Textzeile zu beenden.

In diesem speziellen Fall kann das, was Sie versuchen, mit einer einzigen -e ...Klausel erreicht werden. Sie müssen den aBefehl nur richtig verwenden. Nach dem POSIX-Standard amuss nach a \eine neue Zeile eingefügt werden, danach wird der Rest der nächsten Zeile eingefügt (bis eine neue Zeile oder das Ende der -eKlausel angetroffen wird). Sie könnten also Folgendes tun:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt

2
Wenn Sie herausfinden möchten, auf welche Funktionen Sie sich bei Gnu-Ismen verlassen, hilft möglicherweise folgende Seite: wiki.alpinelinux.org/wiki/Regex . Es ist jedoch nur auf die Regex-Funktionen beschränkt. nicht die anderen sed befehle.
dubiousjim

5

GNU sed wird anscheinend viel häufiger verwendet als POSIX sed, basierend auf Beispielen / Tutorials, die ich sowieso verwendet habe.

Ich habe festgestellt, dass es viel einfacher ist, GNU sed auf jedem OSX-Rechner zu installieren, den ich verwende. Wenn Sie interessiert sind, installieren Sie es am besten über Homebrew .

Sie können entweder nur $ brew install gnu-sedoder alle gängigen GNU-Dienstprogramme mit $ brew install coreutils.

Wenn Sie dann auf ein Syntaxproblem mit dateeinem anderen Programm stoßen, können Sie einfach die GNU-Version verwenden. Irgendwann habe ich einfach festgestellt, dass es einfacher ist, die GNU-Versionen immer zu verwenden und sie früher als die Systemversionen in meine zu stellen PATH.


5

Perl leidet nicht unter solchen plattformabhängigen Unterschieden zwischen GNU und Nicht-GNU und unter "proprietären" Eigenheiten. Du könntest es tun:

perl -ni.old -e 'print;if ($.==4) {print "mode 0755\n"}' file

Das -n' option creates a loop that reads every line of the input file. Unlike its cousin-p (not used here) it doesn't automatically print every line read. The-i invokes in-place replacement. The argument to-i (viz. ".old") can be dropped or changed. It leaves a backup of the unmodified file. The -esignalisiert den Beginn des Skripts.

Das $.kennzeichnet die Zeilennummer, beginnend mit Zeile-1. Die Befehlszeile lautet also 'file' und wenn die Zeilennummer gleich 4 ist, wird diese Zeile gedruckt, gefolgt von dem, was Sie einfügen möchten.

Ich würde gerne hinzufügen, dass Perl seine Wurzeln sedAWK und C verdankt , sodass die Syntax für einfache Substitutionen usw. keine sehr steile Lernkurve darstellt.


Gute Antwort! Aber Sie können es ein bisschen vereinfachen. Versuchen Sie folgendes: perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txt. Der -pSchalter funktioniert hier gut (da wir jede Zeile drucken wollen); Wir müssen nur die Logik von "Drucken nach Zeile 4" auf "Drucken vor Zeile 5" ändern . Und der -lSchalter bewirkt, dass "\ n" automatisch mit jedem gedruckt wird print, sodass Sie es nicht selbst drucken müssen.
JL
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.