[gelöst] SED in einer Zeile nur einen Bereich bearbeiten

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
k2
Beiträge: 110
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

[gelöst] SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von k2 » 08.02.2019 16:24:09

Hi, ich und SED und schon wieder...

Gibt es mit sed einen Weg, in einer gefundenen Zeile nur innerhalb eines Bereiches Ersetzungen durchzuführen?
Konkret sollen nur innerhalb einer URL die Leerzeichen in Minus umgewandelt werden; allerdings ist die Anzahl der Leerzeichen nicht bekannt, da der Text der URL nicht bekannt ist.
Beispiel:
<li><a href="Integration with external libraries and tools.md.html">Integration with external libraries and tools</a></li> solte zu
<li><a href="Integration-with-external-libraries-and-tools.md.html">Integration with external libraries and tools</a></li> werden.

Meine hilflosen Versuche...

Code: Alles auswählen

sed -i '/<li><a href="/,/">/y/ /-/' index.html
# ersetzt natürlich alle Leerzeichen, somit gibt es z.B.: a-href

sed -i '/<li><a href=".*">/s/<a href="\(.*\) \(.*\)">/<a href="\1-\2">/g' index.html
# ersetzt nur das letzte Leerzeichen, da \(.*\) sich auf das Maximum ausdehnt
Vielleicht geht es aber mit sed auch gar nicht in einer gefundenen Zeile noch einen Bereich auszuwählen und nur diesen zu bearbeiten...

Grüße
Zuletzt geändert von k2 am 10.02.2019 16:56:23, insgesamt 1-mal geändert.

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

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von tobo » 08.02.2019 20:10:59

Vermutlich suchst du sowas:

Code: Alles auswählen

sed -r ':LABEL;s/(href="[^ "]*) +/\1-/;t LABEL'
Nimmt href=" als Anker, geht von dort zu den nächsten (aufeinanderfolgenden) Leerzeichen - solange kein " im Weg ist - und ersetzt das/die Leerzeichen durch -. Dieser Schritt wiederholt sich, solange die Ersetzung erfolgt.

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

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von Meillo » 08.02.2019 21:12:40

@tobo: Gute Idee. Gefaellt mir. :THX:


Hier ein alternativer Ansatz (der nichts besser macht als tobos Vorschlag):

Code: Alles auswählen

sed '/href=/{h;s,.*href=",,;s,".*,,;s, ,-,g; G;s,\(.*\)\n\(.*href="\)[^"]*\(.*\),\2\1\3,;}'
Erklaerung:

- Wenn /href=/ in der Zeile steht, dann:
- Kopiere die Zeile in den Holdspace (um sie spaeter noch zu haben)
- Entferne alles vor und nach der URL
- Dann ersetze alle Leerzeichen durch Minuse (in der URL, denn nur noch sie steht in der Zeile)
- Haenge den Holdspace wieder an (jetzt hat man die bearbeitete URL, ein Newline und die Originalzeile)
- Die letzte Ersetzung baut das passend zusammen, indem sie die Teile (korrgierte URL; vor der URL; hinter der URL) umsortiert und die urspruengliche URL wegfallen laesst

Einschraenkung: Es darf nur ein ``href='' in der Zeile vorhanden sein. (Tobos Loesung funktioniert AFAICS auch mit mehreren ... sie ist eben ueberhaupt besser. ;-) )
Use ed(1) once in a while!

k2
Beiträge: 110
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von k2 » 09.02.2019 09:53:40

Hi,

auf die Vorgehensweise mit dem Holdspace und dem schrittweisen Abarbeiten bn ich schon via Suchmaschine gestoßen, konnte es aber nicht nachvollziehen. Danke für Eure Vorschläge, ich werde sie jetzt gleich mal durchackern - bis es "klick" macht!

Danke!

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

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von Meillo » 09.02.2019 21:16:38

k2 hat geschrieben: ↑ zum Beitrag ↑
09.02.2019 09:53:40
Danke für Eure Vorschläge, ich werde sie jetzt gleich mal durchackern - bis es "klick" macht!
Ja, versuche dich daran. Wenn du Hilfe brauchst, um ein Schrittchen weiter zu kommen, dann melde dich. Am besten ist es schon, wenn du es dir selber erarbeitest, aber manchmal braucht man einfach jemanden, der einem ueber eine schwierige Stelle hilft. Dafuer sind wir gerne da.
Use ed(1) once in a while!

k2
Beiträge: 110
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von k2 » 10.02.2019 16:35:22

Hi, Danke Euch!

tobos Vorschlag habe ich umgesetzt, er funktioniert. Nochmals kurz zu meinem Verständnis:
a) Die Ersetzung im Loop ist klar.
b) -r brauche ich für den Einsatz der extendedRegEx damit das + (für mindestens einmaligen Vorkommens des vorhergehenden Zeichens) funktioniert?
c) ich habe keinen Einfluss darauf, wo in der Zeile das Label gesetzt wird? Das Label markiert nur die Zeile? Die Doku schweigt hierzu - oder ich habe sie nicht kappiert...
d) Ich konnte keine Zeilen adressieren vor dem Label setzen, SED meldet: »:« erwartet keine Adressen.
Ist aber kein Problem, das Script arbeitet!

Meillos Vorschlag funktioniert ebenfalls, alles klar damit; das Leerzeichen vor dem Get ' G' brauche ich aber offensichtlich nicht, es stört aber auch nicht.

Beide Lösungen konnte ich nachvollziehen, das war wieder etwas für die eingerosteten Gehirnwindungen ... https://de.wikipedia.org/wiki/Brainfuck

Ursprünglich wollte ich nur in einem Github-Wiki https://github.com/gnab/remark/wiki lesen --- dann das zur Offline-Nutzung downloaden --- dann nur noch mit Pandoc in HTML übersetzen --- und dann war dort keine index.html zur Navigation vorhanden, sondern nur eine völlig unbrauchbare _Sidebar.md. Jetzt habe ich ein Quick-and-Dirty-Script, welches mir das Github-Wiki in ein HTML-Wiki übersetzt. Brauche ich vermutlich nie wieder, aber ich habe viel über SED gelernt! Danke Euch! :D

Hier der Code - ich habe ihn allerdings nicht mit anderen Github-Wikis getestet

Code: Alles auswählen

#!/bin/bash
# gitwiki2html.sh
# macht aus einem Github.md-Wiki md ein HTML-Wiki
# Script im Wiki-Verzeichnis mit den md-Dateien starten

htmldir=${PWD##*/}.html
mkdir ${htmldir}
for file in *.md
do
	pandoc -f markdown_github -t html5 -s -o ${htmldir}/${file}.html ${file}
done
mv ${htmldir}/_Sidebar.md.html ${htmldir}/index.html

sed -i '/<li>\[\[.*|.*#.*\]\]<\/li>/s/<li>\[\[\(.*\)|\(.*\)#\(.*\)\]\]<\/li>/<li><a href="\2.md.html#\3">\1<\/a><\/li>/' ${htmldir}/index.html
sed -i '/<li>\[\[.*\]\]<\/li>/s/<li>\[\[\(.*\)\]\]<\/li>/<li><a href="\1.md.html">\1<\/a><\/li>/' ${htmldir}/index.html
sed -i '/<li>\[\[.*|.*#.*\]\]/s/<li>\[\[\(.*\)|\(.*\)#\(.*\)\]\]/<li><a href="\2.md.html#\3">\1<\/a>/' ${htmldir}/index.html
sed -i -r ':LABEL;s/(href="[^ "]*) +/\1-/;t LABEL' ${htmldir}/index.html
# funktioniert ebenfalls
# sed -i '/href=/{h;s,.*href=",,;s,".*,,;s, ,-,g;G;s,\(.*\)\n\(.*href="\)[^"]*\(.*\),\2\1\3,;}' ${htmldir}/index.html

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

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von tobo » 10.02.2019 23:25:26

k2 hat geschrieben: ↑ zum Beitrag ↑
10.02.2019 16:35:22
b) -r brauche ich für den Einsatz der extendedRegEx damit das + (für mindestens einmaligen Vorkommens des vorhergehenden Zeichens) funktioniert?
In der GNU-Version wirkt extended sich auf die Zeichen +?({)}| aus. Hauptsächlich verwendet man extended, um das umständliche und "unübersichtliche" gequote der Klammern zu umgehen. Da hier aber nur ein Paar ist, kann man sich das eigentlich auch schenken. Das + funktioniert in GNU-BRE mit \+ ebenso - ob das jetzt aber POSIX-technisch abgedeckt ist, da bin ich mir nicht sicher. Grundsätzlich kannste das aber sowieso ersetzen: Ein A+ ist gleich einem AA*.

Code: Alles auswählen

sed ':LABEL;s/\(href="[^ "]*\)  */\1-/;t LABEL'
Wobei mir gerade auffällt, dass in meinen heirloom-sed keine Labels funktionieren!?
c) ich habe keinen Einfluss darauf, wo in der Zeile das Label gesetzt wird? Das Label markiert nur die Zeile? Die Doku schweigt hierzu - oder ich habe sie nicht kappiert...
Verstehe ich vermutlich falsch: Das Label wird nicht in der Zeile für den Text gesetzt, sondern für das Skript. Also den Sed-Anweisungsteil, die Kommandos.
d) Ich konnte keine Zeilen adressieren vor dem Label setzen, SED meldet: »:« erwartet keine Adressen.
Ist aber kein Problem, das Script arbeitet!
Ja. Sed kennt Kommandos, die keine, eine oder zwei Adressen annehmen. Kannst du aber üblicherweise umgehen, indem du es klammerst:

Code: Alles auswählen

sed '/href="/{:LABEL;s/\(href="[^ "]*\)  */\1-/;t LABEL}'
Jetzt nur beispielhaft herausgegriffen. Sowas hier macht man eigentlich nicht - die Variablen quotet man beim Auslesen, sofern nichts dagegen spricht. Dann hast du 95% der üblichen Fehler wegen Leervariablen, Leerzeichen und Sonderzeichen abgedeckt.

Code: Alles auswählen

mkdir ${htmldir}
pandoc -f markdown_github -t html5 -s -o ${htmldir}/${file}.html ${file}
mv ${htmldir}/_Sidebar.md.html ${htmldir}/index.html

Code: Alles auswählen

mkdir "${htmldir}"
pandoc -f markdown_github -t html5 -s -o "${htmldir}"/"${file}".html "${file}"
mv "${htmldir}"/_Sidebar.md.html "${htmldir}"/index.html

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

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von Meillo » 11.02.2019 09:05:04

tobo hat geschrieben: ↑ zum Beitrag ↑
10.02.2019 23:25:26
Das + funktioniert in GNU-BRE mit \+ ebenso - ob das jetzt aber POSIX-technisch abgedeckt ist, da bin ich mir nicht sicher.
POSIX BRE kennen kein Plus, das ist eine GNU-Extension. ``\+'' ist in POSIX sed undefined behavior. Siehe http://pubs.opengroup.org/onlinepubs/96 ... #tag_09_03

Code: Alles auswählen

sed ':LABEL;s/\(href="[^ "]*\)  */\1-/;t LABEL'
Wobei mir gerade auffällt, dass in meinen heirloom-sed keine Labels funktionieren!?
Sie funktionieren schon, aber Labels gehen dort bis ans Zeilenende, man kann sie nicht einfach mit einem Strichpunkt abschliessen. Interessanterweise ist das auch bei der POSIX-Binary von Heirloom-sed so. Man muss das Label mit einem Newline abschliessen, dann funktioniert es:

Code: Alles auswählen

sed ':LABEL
s/\(href="[^ "]*\)  */\1-/;t LABEL'
Auf der POSIX-Seite habe ich leider nichts dazu gefunden. Keine explizite Vorgabe und noch nicht mal ein Hinweis ... das finde ich ungewoehnlich. Auch die Manpage von Heirloom sed enthaelt keinen Hinweis.


Dann halt ein Blick in den Code ...

Leider kann man den Code der Heirloom-Toolchest auf Sourceforge nicht mehr online anschauen, weil die ihre CVS-Webview abgeschaltet haben. :-( Darum die relevante Datei im Pastebin: pastebin/?mode=view&s=40619 In Zeile 333 wird das Label gelese und dort steht:

Code: Alles auswählen

while((*tp++ = *cp++))
`cp' ist der Linebuffer, in dem das Kommando steht. Bei einem Label liest er also bis zum Stringende, was dem Zeilenende entspricht. Hier muesste noch ein ``&& *cp != ';''' in der Schleifenbedingung stehen.

Dann ein Blick auf GNU sed. Da kann man den Code online browsen. Die relevante Stelle ist: http://git.savannah.gnu.org/cgit/sed.gi ... ile.c#n671 Hier wird das Label durch eine ganze Reihe von Zeichen begrenzt, neben Newline und Strichpunkt auch durch ein Kommentarzeichen oder Whitespace.

Btw: Trailing Whitespace bei Labels wegzuschneiden widerspricht POSIX, ist aber sinnvoll. POSIX hat sich hier IMO schlecht entschieden, da sie eh schon schreiben, dass es schlechte Programmierpraxis ist und nur theoretisch existierende Scripte bricht. Die Scripte, die sowas ernsthaft nutzen sollten vielleicht besser gebrochen werden, damit sie repariert werden.
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html hat geschrieben: The b, t, and : commands are documented to ignore leading white space, but no mention is made of trailing white space. Historical implementations of sed assigned different locations to the labels 'x' and "x ". This is not useful, and leads to subtle programming errors, but it is historical practice, and changing it could theoretically break working scripts. Implementors are encouraged to provide warning messages about labels that are never referenced by a b or t command, jumps to labels that do not exist, and label arguments that are subject to truncation.

An dieser Stelle von mir ein Dank fuer die nette Motivation mal wieder in den Code zu schauen. Solltet ihr auch hin und wieder machen! :THX:
Zuletzt geändert von Meillo am 11.02.2019 10:52:32, insgesamt 1-mal geändert.
Grund: Zeilennummer korrigiert
Use ed(1) once in a while!

RobertDebiannutzer
Beiträge: 329
Registriert: 16.06.2017 09:52:36

Re: [gelöst] SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von RobertDebiannutzer » 11.02.2019 10:45:37

Meillo hat geschrieben: ↑ zum Beitrag ↑
11.02.2019 09:05:04
Zeile 33
333
Pointer arithmetic ist so schön! :THX:

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

Re: [gelöst] SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von Meillo » 11.02.2019 10:54:11

RobertDebiannutzer hat geschrieben: ↑ zum Beitrag ↑
11.02.2019 10:45:37
Meillo hat geschrieben: ↑ zum Beitrag ↑
11.02.2019 09:05:04
Zeile 33
333
Danke. Hab's korrigiert.

Pointer arithmetic ist so schön! :THX:
;-)
Use ed(1) once in a while!

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

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von tobo » 11.02.2019 13:23:34

Meillo hat geschrieben: ↑ zum Beitrag ↑
11.02.2019 09:05:04

Code: Alles auswählen

sed ':LABEL;s/\(href="[^ "]*\)  */\1-/;t LABEL'
Wobei mir gerade auffällt, dass in meinen heirloom-sed keine Labels funktionieren!?
Sie funktionieren schon, aber Labels gehen dort bis ans Zeilenende, man kann sie nicht einfach mit einem Strichpunkt abschliessen. Interessanterweise ist das auch bei der POSIX-Binary von Heirloom-sed so. Man muss das Label mit einem Newline abschliessen, dann funktioniert es:
Habe das gestern Abend unter anderem nochmal neukompiliert!? Ich war mir nämlich eigentlich sicher, dass das bei mir schon mal funktionierte und außerdem kann das ja auch gar nicht sein, dass das bei heirloom nicht funktioniert!? Das Zeug von denen kann man ja normal als Referenz hernehmen. Aber darauf bin ich jetzt nicht gekommen - Vielen Dank, Meillo.

k2
Beiträge: 110
Registriert: 14.08.2002 22:29:28
Lizenz eigener Beiträge: MIT Lizenz

Re: SED in einer Zeile nur einen Bereich bearbeiten

Beitrag von k2 » 13.02.2019 11:11:24

tobo hat geschrieben: ↑ zum Beitrag ↑
10.02.2019 23:25:26
Sowas hier macht man eigentlich nicht - die Variablen quotet man beim Auslesen, sofern nichts dagegen spricht. Dann hast du 95% der üblichen Fehler wegen Leervariablen, Leerzeichen und Sonderzeichen abgedeckt.

Code: Alles auswählen

mkdir ${htmldir}
pandoc -f markdown_github -t html5 -s -o ${htmldir}/${file}.html ${file}
mv ${htmldir}/_Sidebar.md.html ${htmldir}/index.html

Code: Alles auswählen

mkdir "${htmldir}"
pandoc -f markdown_github -t html5 -s -o "${htmldir}"/"${file}".html "${file}"
mv "${htmldir}"/_Sidebar.md.html "${htmldir}"/index.html
Ja, da hast Du Recht - das war wirklich mal wieder Quick & Dirty - Danke für den Hinweis. Man (ich) sollte auch bei kleinen Scripts nicht so nachlässig sein...
Meillo hat geschrieben: ↑ zum Beitrag ↑
11.02.2019 09:05:04
mal wieder in den Code zu schauen. Solltet ihr auch hin und wieder machen!
Uiuiui - da fehlt's noch gaaanz weit bei mir...

Nochmals Danke an alle!

Antworten