|
Dalai
Anmeldungsdatum: 16. Juni 2008
Beiträge: 2321
|
Hallo, liebe Anhänger der gepflegten (Skript)Programmierung. Ich hab mal wieder eine eine Problemstellung, bei der ich hoffe, dass mir die Experten behilflich sein können, vor allem diejenigen, die sich mit sed auskennen. Gegeben ist beispielhaft eine Konfigurationsdatei mit folgendem Inhalt: foo something
foo someotherthing
foo somesomething
foo thingthing
# hier weitere Zeilen Es können wie gezeigt mehrere Zeilen enthalten sein oder auch nur eine. Die Kommentarzeilen am Ende sind immer enthalten, haben aber unterschiedlichen Inhalt. Ziel ist es, alle diese Zeilen auszukommentieren und nach diesen, aber noch vor dem folgenden Kommentar, eine Leerzeile und eine oder mehrere weitere Zeilen einzufügen. Beispielhaft soll das dann so aussehen: #foo something
#foo someotherthing
#foo somesomething
#foo thingthing
neue Zeile
# hier weitere Zeilen Solange nur eine der gesuchten Zeilen enthalten ist, klappt der folgende sed-Aufruf wunderbar: | sed -r "/^foo.+thing/ {
s|^|#|; a\\
neue Zeile
}" file.txt
|
Aber Ziel ist ja nicht, für jeden Match des Ausdrucks eine Zeile einzufügen sondern nur für den letzten. Ich bin sicher, dass das (recht einfach) geht, aber ich weiß nicht wie. Grüße Dalai
|
|
shiro
Supporter
Anmeldungsdatum: 20. Juli 2020
Beiträge: 1303
|
Meinst du so etwas?
$ echo "foo something
foo someotherthing
foo somesomething
foo thingthing
# hier weitere Zeilen
" | sed '/^#/ i\neue Zeile\n
s/foo.\+thing/#&/'
#foo something
#foo someotherthing
#foo somesomething
#foo thingthing
neue Zeile
# hier weitere Zeilen
$
|
|
Dalai
(Themenstarter)
Anmeldungsdatum: 16. Juni 2008
Beiträge: 2321
|
Ich hätte wohl den Inhalt der Datei etwas besser darlegen müssen. Es befinden sich über den auszukommentierenden Zeilen weitere Zeilen, darunter auch Kommentare: # Kommentar
Konfigurationsdirektive
# Kommentar
foo something
foo someotherthing
foo somesomething
foo thingthing
# hier weitere Zeilen
Grüße
Dalai
|
|
juribel
Anmeldungsdatum: 20. April 2014
Beiträge: 1269
|
GANZ GENAU bitte! Ich verstehe das so: Du möchtest den Block mit den foo-Zeilen auskommentieren, aber nicht weil da foo drinsteht, sondern weil sie nicht auskommentiert sind, und weil dieser Block von zwei Leerzeilen umrahmt ist. Richtig? Dann möchtest du diesen Block auskommentieren, die Leerzeilen aber so lassen, und dahinter einen neuen Block oder eine neue Zeile, gefolgt von einer neuen Leerzeile, einfügen. Richtig? Sowas würde ich nicht in einen Einzeiler vomöglich mit sed oder awk packen wollen. Dann brauchst du nämlich locker 20 Zeilen Beschreibung, um das Ganze nach einer Woche überhaupt noch zu verstehen. Das wäre schon ein richtiges Perl-Progrämmelchen wert, genau für so etwas ist Perl da. Mir fehlt dafür aber die Zeit.
|
|
Dalai
(Themenstarter)
Anmeldungsdatum: 16. Juni 2008
Beiträge: 2321
|
OK, ich versuche es etwas detaillierter zu beschreiben. Ist-Zustand: # Kommentar
Konfigurationsdirektive
# Kommentar
foo something
foo someotherthing
foo somesomething
foo thingthing
# hier weitere Zeilen Soll-Zustand:
# Kommentar
Konfigurationsdirektive
# Kommentar
#foo something
#foo someotherthing
#foo somesomething
#foo thingthing
neue Zeile
# hier weitere Zeilen Die Zeile(n) soll(en) auskommentiert werden, weil sie Konfigurationsdirektiven enthalten, die ich überstimmen will. Die Direktiven sollen inhaltlich nicht verändert werden, nur auskommentiert. Diese Zeilen müssen anhand des Inhalts - hier /^foo.+thing/ - erkannt werden, die Zeilen drumherum sind mehr oder weniger immer anders. Die hinzuzufügende Zeile enthält die meinen Wünschen entsprechende Konfigurationsdirektive. Alle anderen Zeilen sollen komplett unberührt bleiben. Kurzform:
Zeile(n) mit /^foo.+thing/ auskommentieren. Nach der/den gerade auskommentierten Zeile(n) eine Leerzeile und eine Zeile mit Inhalt einfügen. Der Rest der Datei muss unverändert bleiben.
Grüße
Dalai
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11278
|
Wenn ich das richtig verstehe, reicht sowas:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #!/usr/bin/env python3
import fileinput
import re
SEARCH = re.compile(r"foo.+thing")
NEW_CONTENT = """
Neue Zeile"""
on_foo_block = False
for line in fileinput.input():
if SEARCH.match(line): # https://docs.python.org/3/library/re.html#re.match
on_foo_block = True
print(f"#{line}", end="")
else:
if on_foo_block:
print(NEW_CONTENT)
on_foo_block = False
print(line, end="")
on_foo_block = False
|
python comment_and_modify.py input.txt
# Kommentar
Konfigurationsdirektive
# Kommentar
#foo something
#foo someotherthing
#foo somesomething
#foo thingthing
Neue Zeile
# hier weitere Zeilen
|
|
Dalai
(Themenstarter)
Anmeldungsdatum: 16. Juni 2008
Beiträge: 2321
|
seahawk1986 schrieb: Wenn ich das richtig verstehe, reicht sowas:
Ja, das tut, was ich möchte. Danke! Kann man das mit meinem existierenden Bash-Skript verheiraten, das die einzufügende(n) Zeile(n) dynamisch generiert/zusammenbaut? Grüße
Dalai
|
|
shiro
Supporter
Anmeldungsdatum: 20. Juli 2020
Beiträge: 1303
|
Ok, mit den geänderten Randbedingungen auch eine "sed" Lösung:
$ cat ist.txt | sed '/^foo.\+thing/{bb;:a;n;:b;s/^foo.*thing/#&/;ta;a\Neue Zeile\n
}'
# Kommentar
Konfigurationsdirektive
# Kommentar
#foo something
#foo someotherthing
#foo somesomething
#foo thingthing
Neue Zeile
# hier weitere Zeilen
$
Information:
ist.txt ist deine Vorgabedatei sed übernimmt alle Zeilen, bis die Zeile "^foo.\+thing" auftaucht. Nun werden alle mit foo.\+thing beginnenden Zeilen bearbeitet (auskommentiert), indem ein Branch (b) to Label b (:b) erfolgt. Es wird ein Substitute (s) der "foo.\+thing" Zeichenkette derart ausgeführt, dass ein Kommentarzeichen (#) davor gesetzt wird (#&). Ist die Substitution erfolgreich durchgeführt worden, springe (t) zum Label a (:a). Lese die nächste Zeile ein (n) und mache die nächste Substitution War die Substitution nicht erfolgreich (Zeile nach foo.\+thing), füge den Text (a) "Neue Zeile\n" ein.
Hierauf werden alle Zeilen ausgegeben, bis wieder eine "foo.\+thing" Zeile gefunden wird und der obige Ablauf sich wiederholt.
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11278
|
Dalai schrieb: Kann man das mit meinem existierenden Bash-Skript verheiraten, das die einzufügende(n) Zeile(n) dynamisch generiert/zusammenbaut?
Ja, man könnte das aber auch gleich komplett in der Bash machen: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | #!/usr/bin/bash
search="^foo.+thing"
new_content=$"\nNeue Zeile"
declare -i on_block=0
while IFS= read -r line
do
if [[ $line =~ $search ]]
then
on_block=1
echo "#${line}"
else
[ "$on_block" = 1 ] && echo -e "$new_content"
echo "$line"
on_block=0
fi
done < input.file
|
|
|
TK87
Anmeldungsdatum: 8. Juli 2019
Beiträge: Zähle...
|
Moin, oder statt sed einfach perl nehmen zum ersetzen. Dessen RegEx Flavour bietet deutlich mehr Möglichkeiten. Als singleliner:
| perl -i -0pe 's/^(?=foo)/#/mg; s/(.*^#foo[^\n]+)/$1\n\n--- Neue Zeile ---/sm' input.file
|
Gruß Thomas
|
|
shiro
Supporter
Anmeldungsdatum: 20. Juli 2020
Beiträge: 1303
|
Danke TK87 für die Perl regex. Sie ist interessant zu lesen. Aber nach meiner Interpretation würde dann nur vor dem letzten Kommentar der Text "–- Neue Zeile –-" eingefügt werden. Ich hatte die Anforderung so interpretiert, dass nach dem "foo" Block die Zeile kommen sollte. Da ist allerdings in der Anforderung für mich nicht klar ersichtlich gewesen. Na ja, so hat der TS zwei Lösungen und kann sich die aussuchen, die er braucht.
|
|
TK87
Anmeldungsdatum: 8. Juli 2019
Beiträge: Zähle...
|
shiro schrieb: Aber nach meiner Interpretation würde dann nur vor dem letzten Kommentar der Text "–- Neue Zeile –-" eingefügt werden.
Nein, die Zeile wird hinter den auskommentieren foo-Block eingefügt. s/^(?=foo)/#/mg
Ergänzt eine # am Zeilenanfang bei allen Zeilen, die mit foo beginnen. s/(.*^#foo[^\n]+)/$1\n\n--- Neue Zeile ---/sm
Alles bis zur letzen auskommentierten foo-Zeile wird zur Gruppe "$1" zusammengefasst und durch $1 (also sich selbst) plus der Ergänzung dahinter ersetzt.
|
|
Dalai
(Themenstarter)
Anmeldungsdatum: 16. Juni 2008
Beiträge: 2321
|
shiro schrieb: Ok, mit den geänderten Randbedingungen auch eine "sed" Lösung:
Danke! Hab ich erfolgreich getestet. Lässt sich auch gut in das vorhandene Bash-Skript einbauen. Vielen Dank auch für die leicht verständlichen und nachvollziehbaren Erläuterungen! seahawk1986 schrieb: Ja, man könnte das aber auch gleich komplett in der Bash machen:
In der Tat. Das erinnert mich - mal wieder - daran, dass ich zu selten von den vielfältigen Möglichkeiten der Bash Gebrauch mache. Aber sed kommt eben sehr oft zuerst in den Sinn, wenn es um Search&Replace geht. Anyway, der Code funktioniert ebenfalls mit der fraglichen Konfigurationsdatei. Danke für den Vorschlag! TK87 schrieb: oder statt sed einfach perl nehmen zum ersetzen.
Danke für den Vorschlag.
Dessen RegEx Flavour bietet deutlich mehr Möglichkeiten.
Naja, es geht nicht nur darum, ob man mehr Möglichkeiten hat, sondern auch um andere Dinge wie Lesbarkeit&Verständlichkeit des Codes, Wartbarkeit, Geschwindigkeit (hier an der Stelle komplett irrelevant) und ggf. Verfügbarkeit der Tools. Gerade in Bezug auf Lesbarkeit sind die anderen Vorschläge besser. Auch dieser Vorschlag funktioniert mit der fraglichen Konfigurationsdatei. Der Bash-Code ist für mich am besten verständlich und in das vorhandene Bash-Skript integrierbar und für die gestellte Aufgabe eine gute Lösung. Vielen Dank an alle Helfer für die Beteiligung! Grüße
Dalai
|