Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Du hast Probleme mit Deinem eMail-Programm, Webbrowser oder Textprogramm? Dein Lieblingsprogramm streikt?
Antworten
Richard
Beiträge: 639
Registriert: 11.10.2012 14:18:37
Lizenz eigener Beiträge: GNU General Public License

Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Richard » 22.09.2018 11:33:25

Hallo,

ich möchte einen Text in eine Textdatei immer in die vorletzte Zeile einfügen oder - wenn es einfacher umzusetzen ist - vor eine Zeile mit einem bestimmten Muster. Die letzte Zeile hat immer den gleichen Text. Hier http://www.gtkdb.de/index_7_2917.html hab ich etwas für sed gefunden, nur geht das mit einem Text nicht, evtl. da die letzte Zeile bei mir Leerzeichen enthält. Diese lautet

Code: Alles auswählen

rm -f /tmp/the.lock
Weiter vorne wird ein Lockfile angelegt um zu verhindern, dass das Script doppelt startet. Am Ende soll es wieder gelöscht werden. Füge ich das 1:1 in das verlinkte Bsp. ein erhalte ich

Code: Alles auswählen

sed -i 's/^rm -f /tmp/the.lock/\/usr\/local\/bin\/shutdownbutton.py \&\n\n&/' /etc/rc.local
sed: -e Ausdruck #1, Zeichen 15: Unbekannte Option für »s«
Was mach ich hier falsch? Ich hab auch versucht vor alle Leerzeichen zu maskieren, ändert aber nichts.

Richard

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von eggy » 22.09.2018 11:57:16

Dinge die "Zustände" oder "Zählungen" voraussetzen behandle ich in der Regel lieber mit awk:

Code: Alles auswählen

awk 'BEGIN{line="erstezeile"} {if (NR>1){print line;} line = $0} END{print"hallo"; print line;}'
Der Code zwischen den {} wird mit jeder gelesenen Zeile ausgeführt, der Code in BEGIN{} am Anfang des Scripts, der in END{} ganz am Ende. NR ist die Anzahl der gelesenen Zeile.
Jede Zeile wird in eine Variable gelesen, und jeweils (abgesehn vom ersten Durchlauf) dann ausgegeben wenn die nächste Zeile gelesen wurde. Am Ende haben wir aber die letzte Zeile noch nicht ausgegeben, das ist der Zeitpunkt um Deine vorletzte Zeile auszugeben, und dann noch den Inhalt der letzten Zeille anzeigen, fertig.

Edit: Erklärtext zum Code

Benutzeravatar
detix
Beiträge: 1699
Registriert: 07.02.2007 18:51:28
Wohnort: MK

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von detix » 22.09.2018 12:07:40

oder:
sed ist es eigentlich egal welches Trennzeichen für s/// benutzt wird,
es geht zB auch s### was in diesem Fall auch angebracht wäre, da brauchst du die „/” nicht mit „\” entwerten:

Code: Alles auswählen

sed 's#^rm -f /tmp/the.lock#/usr/local/bin/shutdownbutton.py \&\n\n&#' /etc/rc.local # Test
sed -i 's#^rm -f /tmp/the.lock#/usr/local/bin/shutdownbutton.py \&\n\n&#' /etc/rc.local
Gruß an alle Debianer, und immer daran denken:
Macht ohne Haftung funktioniert nicht!

tobo
Beiträge: 1964
Registriert: 10.12.2008 10:51:41

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von tobo » 22.09.2018 12:12:27

Richard hat geschrieben: ↑ zum Beitrag ↑
22.09.2018 11:33:25

Code: Alles auswählen

sed -i 's/^rm -f /tmp/the.lock/\/usr\/local\/bin\/shutdownbutton.py \&\n\n&/' /etc/rc.local
sed: -e Ausdruck #1, Zeichen 15: Unbekannte Option für »s«
Was mach ich hier falsch?
Die unbekannte Option ist t, denn das ist dein Substitutionsausdruck:

Code: Alles auswählen

s/^rm -f /tmp/t
Entweder du quotest hier genauso die Schrägstriche wie im Erstzungstext bei \/usr\/local\/bin... oder du verzichtest ganz darauf und nimmst ein anderes (eindeutiges) Zeichen für die Abgrenzung zum Substitutionsbefehl s#XXX#YYY#. Der . bedeutet im Suchausdruck ein beliebiges Zeichen, muss also gequotet werden, wenn er literal gilt.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Meillo » 22.09.2018 20:42:48

Wie waer's damit:

Code: Alles auswählen

sed '$i/usr/local/bin/shutdownbutton.py'
:-)
Use ed once in a while!

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von rendegast » 23.09.2018 07:56:28

Richard hat geschrieben: immer in die vorletzte Zeile einfügen
Du kannst die Datei trennen
cat Datei | head -n -1
cat Datei | tail -n 1
und dann mit Deinem Template zusammenfügen
cat head.part dein.template tail.part > ausgabe

oder - wenn es einfacher umzusetzen ist - vor eine Zeile mit einem bestimmten Muster.
Ein Aufteilen nach Muster kann mit 'split' erfolgen.
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

Richard
Beiträge: 639
Registriert: 11.10.2012 14:18:37
Lizenz eigener Beiträge: GNU General Public License

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Richard » 23.09.2018 09:31:42

Ich hab es jetzt so gemacht

Code: Alles auswählen

sed -i 's#^rm -f /tmp/the.lock#/usr/local/bin/shutdownbutton.py \&\n\n&#' /etc/rc.local
Ist es auch möglich in den Ersetzungstext eine Variable einzusetzen die vorher mit Inhalt gefüllt wird? Das geht bisher nicht, es wird aber auch kein Fehler ausgegeben. Ich habe es mal testweise so versucht

Code: Alles auswählen

sed -i 's#^rm '$var' /tmp/the.lock#/usr/local/bin/shutdownbutton.py \&\n\n&#' /etc/rc.local
Der Ersetzungstext ist so natürlich sinnlos, ich wollte nur die Variable versuchen.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Meillo » 23.09.2018 16:06:42

Man kann es wohl schon als Phaenomen bezeichnen, dass bei sed(1) das s-Kommando alle anderen so deutlich dominiert. Mein Vorschlag mit dem i-Kommando erzeugt im konkreten Fall einen Overhead von genau zwei Zeichen (``$i'') und man muss bei ihm noch nicht einmal escapen (ausser es soll ein Mehrzeiler eingefuegt werden), dennoch verwendet man lieber das s-Kommando. Warum?

Liegt es daran, dass die Kommandos i, a und c bei ihrer Uebernahme von ed(1) nach sed(1) etwas schwerfaellig umgesetzt worden sind (und nur mit Backslash-Newline portabel sind)? Oder liegt es daran, dass der sed-Copy-n-Paster bloss das s-Kommando halbwegs kennt, sonst aber sed(1) fuer Schwarze Magie haelt? Oder liegt es daran, dass das s-Kommando so maechtig ist, dass man es auch in Spezialfaellen verwendet, die eigentlich fuer andere Kommandos geeigneter waeren?

Ich selber verwende i, a und c auch nicht so oft. In vielen solcher Faelle verwende ich lieber awk. Im konkreten Fall erscheint mir mein sed-Vorschlag halt so ideal geeignet und mit dem wenigsten Overhead behaftet, so dass ich mich frage, warum man sich nicht sofort darauf stuerzt? Ich wuerd's gerne verstehen.
Use ed once in a while!

Richard
Beiträge: 639
Registriert: 11.10.2012 14:18:37
Lizenz eigener Beiträge: GNU General Public License

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Richard » 23.09.2018 18:01:44

Ich hab von sed wenig Ahnung, den konkreten Befehl hab ich mir ergoogelt. Leider finde ich keine Möglichkeit da den Text der Variablen einzufügen.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Meillo » 23.09.2018 19:26:24

Hier, wie gewuenscht, die Variante mit s-Befehl mit Variable:

Code: Alles auswählen

var="-f"
sed -i 's#^rm '"$var"' /tmp/the.lock#/usr/local/bin/shutdownbutton.py \&\n\n&#' /etc/rc.local
(Btw: Ich verstehe den Sinn aber nicht so recht. Warum ist das Flag fuer rm(1) in der Variable? Warum bedingst du dadurch die Einfuegung? Ich wuerde eher verstehen, wenn die ganze gesuchte Zeile in der Variable steht.)


Hier die Variante mit i-Befehl mit Variable:

Code: Alles auswählen

var="-f"
sed -i '/^rm '"$var"' \/tmp\/the.lock/i /usr/local/bin/shutdownbutton.py &' /etc/rc.local
Nochmal dasselbe ohne Slash-Escaping:

Code: Alles auswählen

var="-f"
sed -i '\#^rm '"$var"' /tmp/the.lock#i /usr/local/bin/shutdownbutton.py &' /etc/rc.local

... oder, falls vor der letzten Zeile eingefuegt werden soll:

Code: Alles auswählen

sed -i '$i /usr/local/bin/shutdownbutton.py &' /etc/rc.local
... und das noch portabel, wenn man kein GNU sed hat:

Code: Alles auswählen

sed '$i\
/usr/local/bin/shutdownbutton.py &' /etc/rc.local | sort -m -o /etc/rc.local
(Zur Erklaerung: nicht jedes sed kennt `-i', darum wird die Ausgabe nur auf stdout geschrieben. Diesen Output sammelt sort komplett ein, bevor es nach `-o /etc/rc.local' schreibt. `-m' sorgt dafuer, dass sort nicht sortiert. ... das ist etwas tricky, zugegeben, erspart einem aber das Umkopieren mit einer Hilfsdatei, was aber noetig waere, wenn das sed kein `-i' hat.)
Use ed once in a while!

Benutzeravatar
detix
Beiträge: 1699
Registriert: 07.02.2007 18:51:28
Wohnort: MK

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von detix » 23.09.2018 20:34:10

Meillo hat geschrieben:Man kann es wohl schon als Phaenomen bezeichnen, dass bei sed(1) das s-Kommando alle anderen so deutlich dominiert. Mein Vorschlag mit dem i-Kommando erzeugt im konkreten Fall einen Overhead von genau zwei Zeichen (``$i'') und man muss bei ihm noch nicht einmal escapen (ausser es soll ein Mehrzeiler eingefuegt werden), dennoch verwendet man lieber das s-Kommando. Warum?
Das Problem dabei ist (siehe Eingangspost):
...hab ich etwas für sed gefunden, nur geht das mit einem Text nicht,
evtl. da die letzte Zeile bei mir Leerzeichen enthält...

Code: Alles auswählen

sed '$i/usr/local/bin/shutdownbutton.py'
womit /usr/local... nach der Zeile mit rm -f /tmp... geschrieben wird.
Gruß an alle Debianer, und immer daran denken:
Macht ohne Haftung funktioniert nicht!

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Meillo » 23.09.2018 20:53:42

detix hat geschrieben: ↑ zum Beitrag ↑
23.09.2018 20:34:10
Das Problem dabei ist (siehe Eingangspost):
...hab ich etwas für sed gefunden, nur geht das mit einem Text nicht,
evtl. da die letzte Zeile bei mir Leerzeichen enthält...

Code: Alles auswählen

sed '$i/usr/local/bin/shutdownbutton.py'
womit /usr/local... nach der Zeile mit rm -f /tmp... geschrieben wird.
Ich habe die Aussage dort anders verstanden. Aus dem Eingangspost:
Richard hat geschrieben: ↑ zum Beitrag ↑
22.09.2018 11:33:25
Die letzte Zeile hat immer den gleichen Text. Hier http://www.gtkdb.de/index_7_2917.html hab ich etwas für sed gefunden, nur geht das mit einem Text nicht, evtl. da die letzte Zeile bei mir Leerzeichen enthält. Diese lautet

Code: Alles auswählen

rm -f /tmp/the.lock
Er zeigt uns die letzte Zeile: den rm-Befehl. Der enthaelt zwei Leerzeichen. Sofern ist alles stimmig.

Du scheinst verstanden zu haben, dass der rm-Befehl gar nicht in der letzten Zeile waere, sondern in der Vorletzten und dass die letzte Zeile eine leere Zeile waere oder welche, die nur Whitespace enthaelt. Das kann durchaus so sein, dann haette sich Richard aber unpassend ausgedrueckt. Die letzte Zeile ist das was `tail -n 1' ausgibt. Wenn die leer ist oder nur Whitespace enthaelt, ist es trotzdem die letzte Zeile.

Falls man nun aber vor die vorletzte Zeilen einfuegen wollen wuerde, kann man ``sed '$i ...''' natuerlich nicht verwenden. Die Varianten mit Suchtext aus meinem letzten Post gingen aber schon. Nun gut, wahrscheinlich ist die Syntax des i-Befehls wirklich zu unhandlich. Das will ich auch gar nicht bestreiten ...
Use ed once in a while!

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Meillo » 23.09.2018 21:03:04

OT:
Meillo hat geschrieben: ↑ zum Beitrag ↑
23.09.2018 19:26:24
... und das noch portabel, wenn man kein GNU sed hat:

Code: Alles auswählen

sed '$i\
/usr/local/bin/shutdownbutton.py &' /etc/rc.local | sort -m -o /etc/rc.local
(Zur Erklaerung: nicht jedes sed kennt `-i', darum wird die Ausgabe nur auf stdout geschrieben. Diesen Output sammelt sort komplett ein, bevor es nach `-o /etc/rc.local' schreibt. `-m' sorgt dafuer, dass sort nicht sortiert. ... das ist etwas tricky, zugegeben, erspart einem aber das Umkopieren mit einer Hilfsdatei, was aber noetig waere, wenn das sed kein `-i' hat.)
Schoen waer's gewesen ... :roll:

`sort -m -o ...' wuerde laut POSIX schon funktionierten, denn:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sort.html hat geschrieben: -o output

Specify the name of an output file to be used instead of the standard output. This file can be the same as one of the input files.
Doch GNU sort ist dabei nicht so entschlossen:
https://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html#sort-invocation hat geschrieben: ‘-o output-file’
‘--output=output-file’

Write output to output-file instead of standard output. Normally, sort reads all input before opening output-file, so you can sort a file in place by using commands like sort -o F F and cat F | sort -o F. However, it is often safer to output to an otherwise-unused file, as data may be lost if the system crashes or sort encounters an I/O or other serious error while a file is being sorted in place. Also, sort with --merge (-m) can open the output file before reading all input, so a command like cat F | sort -m -o F - G is not safe as sort might start writing F before cat is done reading it.
Warum GNU sort aber mit `-m' hin und wieder (vielleicht wenn gerade Vollmond ist, wer weiss?) das Output-File vor dem Lesen allen Inputs schreibt, verstehe ich nicht. Noetig waere das AFAICS nicht. Das Principle of Least Surprise (POLS) wird auch verletzt.

Nunja, dann implementieren wir uns halt doch das `overwrite'-Script aus ``The UNIX Programming Environment'' (Kapitel 5.5, Seite 152 ff.).

(Notiz an mich: Ich sollte diese spezielle Situation bei verschiedenen sort-Implementierungen untersuchen ... und dann einen Freies-Magazin-Artikel schreiben ... ach, Freies Magazin gibt's ja nicht mehr ... hmm ...)
Use ed once in a while!

Benutzeravatar
detix
Beiträge: 1699
Registriert: 07.02.2007 18:51:28
Wohnort: MK

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von detix » 23.09.2018 21:05:46

Meillo hat geschrieben:Du scheinst verstanden zu haben, dass der rm-Befehl gar nicht in der letzten Zeile waere, sondern in der Vorletzten und dass die letzte Zeile eine leere Zeile waere...
Ja, genau so hatte ich das verstanden, der Author sollte hier mal Klarheit schaffen... :?
Gruß an alle Debianer, und immer daran denken:
Macht ohne Haftung funktioniert nicht!

Richard
Beiträge: 639
Registriert: 11.10.2012 14:18:37
Lizenz eigener Beiträge: GNU General Public License

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Richard » 27.09.2018 13:50:53

Ich hab es mit dem ersten Vorschlag von Meillo umgesetzt:

Code: Alles auswählen

var="-f"
sed -i 's#^rm '"$var"' /tmp/the.lock#/usr/local/bin/shutdownbutton.py \&\n\n&#' /etc/rc.local
Hatte mich hier aber bei der Variablen geirrt, die soll natürlich in den Einfügetext, nicht in den Suchtext. Hab ich geändert und es geht. Ich meinte auch mit "einfügen in die vorletzten Zeile" vor der letzten beschriebenen Zeile. Die letzte (leere) Zeile hatte ich nicht bedacht.

@ Meillo

Was deine Fragen zur Verwendung von sed angeht, versteh ich zu 90% Bahnhof. Ich hab wenig Erfahrung mit sed, hab mir das alles ergoogelt.

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Text in die vorletzte Zeile einfügen oder vor bestimmtem Muster

Beitrag von Meillo » 27.09.2018 14:21:49

Danke fuer die Klarstellungen. Es ist schoen, dass es jetzt klappt. :-)



Eine Sache in diesen Threads ist immer, das Problem zu verstehen und es zu loesen. Ersteres davon ist mehr schwieriger, zweiteres oft einfacher.

Eine zweite Sache ist es, den eigenen Wissensaufbau der Fragenden zu foerdern. Man will schliesslich erreichen, dass beim naechsten Mal ein aehnliches Problem dann selber geloest werden kann und zukuenftig auch Wissen weitergegeben werden kann.

Und eine dritte Sache ist es, selber etwas dabei zu lernen und Spass dabei zu haben. Das ist, was direkt fuer mich dabei rausspringt: Durch deine Frage habe ich mir den i-Befehl von sed nochmal genauer angeschaut und diese ``sort -m -o''-Eigenheit herausgefunden.

Wenn von allen dreien etwas dabei ist, haben alle etwas davon. :THX:
Use ed once in a while!

Antworten