• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

sed mehrfach-Ersetzung in Abhängigkeit von Ausdruck

OsunSeyi

Hacker
Hallo,

Tut mir leid, der Titel ist ziemlich unverständlich...
Eigentlich eine ganz einfache Sache, darum ein Beispiel.

Der Übersichtlichkeit halber wähle ich nicht '/' sondern '|' als Trenner:

Code:
sed 's|\(a\)b\+|\1c|g'

...wird mir bei 'ab' den String 'ac' liefern.

Und bei 'abb' natürlich ebenfalls 'ac' liefern, weil alle Vorkommen von 'b' durch ein 'c' ersetzt werden.

Ich möchte aber gerne alle Vorkommen von 'b' ersetzten, also 'acc' herausbekommen!
Das geht natürlich ganz einfach mit:

Code:
sed 's|b|c|g'

Dies aber wiederum nicht in Abhängigkeit von dem vorausgehenden 'a'.
Das ist genau das Problem.

Ich arbeite mit einem Sed-File und würde es gerne auch mit sed hinbekommen...
 

abgdf

Guru
So?
Code:
echo abb | sed 's/ab+/c/g'
Was machst Du bloß immer für Sachen? :)
Der Schrägstrich ("/") ist doch der normale Trenner für Suchen-/Ersetzen-Operationen. Nicht "|".

Übrigens wundere ich mich selbst, daß das funktioniert.
Eigentlich müßte
Code:
ein "a" und eine beliebige Zahl "b"s
gesucht und durch ein "c" ersetzt werden. Das Ergebnis müßte nach meinem Verständnis daher eigentlich "c" sein, nicht "acc". Na ja. Ich vermeide deshalb RegEx, wo ich kann (meistens kann ich). Sonst kriegt man noch die "Zahnstocherkrankheit" (von den vielen "/ \..\ / /"). Das ist nicht gut für's Gehirn.

Hm, in Perl (meiner bevorzugten Sprache) hab' ich auch recht:
Code:
echo abb | perl -e 'while(<>){s/ab+/c/g; print}'
ergibt "c".
Keine Ahnung, was sed sich da denkt.
 
OP
OsunSeyi

OsunSeyi

Hacker
Jupp, Regexe sind aber interessant 8)

Nicht, daß ich besonders bewandert wäre...

Das Plus muss maskiert werden mit '\+' und der Trenner ist eigentlich egal.
Wenn aber '/' im zu ersetzenden String vorkommt, muss halt ein anderes Zeichen genommen werden...

Aber das beantwortet leider nicht meine Frage, dabei denke ich das kann doch nicht so arg schwer sein. Habe aber bisher keine Lösung gefunden.
 

abgdf

Guru
OsunSeyi schrieb:
Das Plus muss maskiert werden mit '\+'
Ooh, bei den Perl-RegExes würde das bedeuten, daß tatsächlich nach einem "+" gesucht wird.
Offenbar unterscheiden sich Perl und sed hier zu sehr. Dann kann ich nicht mehr mithalten.
OsunSeyi schrieb:
Aber das beantwortet leider nicht meine Frage, dabei denke ich das kann doch nicht so arg schwer sein. Habe aber bisher keine Lösung gefunden.
Mein sed-Vorschlag funktioniert ja. Jetzt müßte man nur noch herausfinden, warum er funktioniert.
 
OP
OsunSeyi

OsunSeyi

Hacker
Code:
~ echo abb | sed 's/ab+/c/g'
abb # keine Ersetzung...

~ echo abbb | sed 's/ab+/c/g'
abbb # keine Ersetzung...

~ echo bbb | sed 's/ab+/c/g'
bbb # keine Ersetzung...

~ echo ab+ | sed 's/ab+/c/g'
c

Ähh... ja ersetzt 'ab+' und sonst nix, zumindestens bei mir!
Ich glaube aber nicht daß es sich verschieden verhält.

Code:
~ echo ab | sed 's/ab\+/c/g'
c

~ echo abbbbbbbb | sed 's/ab\+/c/g'
c

~ echo b | sed 's/ab\+/c/g'
b # keine Ersetzung, passt nicht auf das Muster 'ab\+'

Was ich will ist:

Code:
~ echo ab | sed ...
ac

~ echo abbb | sed ...
accc

~ echo db | sed ...
db  # keine Ersetzung weil kein vorangehendes 'a'
 

abgdf

Guru
Ich glaube, ich hatte mich verlesen. :eek:ps: Weiß auch nicht, wie das passiert ist, aber ich hatte schon ziemlich viel Code geschrieben heute. Vielleicht flimmerten mir inzwischen die Buchstaben vor den Augen.
Du hast recht, was ich gepostet hatte, geht leider gar nicht.

Vielleicht geht das, was Du willst, nicht mit nur einer RegEx.
In Perl würde ich halt zweimal drübergehen:
Code:
#!/usr/bin/perl

my $a = "abb";

if ($a =~ m/ab+/) {
    $a =~ s/b/c/g;
}
print "$a\n";
In sed kann ich Dir leider nicht helfen. :(
 
Hmm, ich würde das Ganze ja sehr pragmatisch angehen: Matche auf ab und ersetze alle b durch c:
Code:
sed '/ab/s/b/c/g'

Dies setzt aber voraus das keine weiteren b in der Zeile vorkommen, die nicht zu ab gehören. Evtl langt dir das ja so. Ansonsten kommen wir mMn in den Bereich der erweiterten RegEx wo wir dann mit so ekeligen Geschichten wie "matche auf a gefolgt von b(mindestens einmal vorkommend), schreibe diese ganzen b in einen anderen Stack, ersetze dort die b durch c, und schreibe zurück in den alten Ausdruck" arbeiten. Da bin ich mir aber auch nicht sicher wie man das macht und müßte wieder Bücher bzw Tutorials wälzen.
Dieses hier mag als vertiefender Einstieg dienen

Oder Du wartest bis robi das hier sieht und dann lernen wir alle noch etwas ;-)
 
@Gräfin Klara,

wo siehst Du nun den weltbewegenden Unterschied zu meiner Lösung? Du matchst auf a als erstes Zeichen ich auf ab. Trotzdem verhindert beides nicht das bei einem "ab xb" hinterher ein "ac xc" raus kommt da der match die ganze Zeile erfasst und die Option g für ein globales Ersetzen sorgt.
 
OP
OsunSeyi

OsunSeyi

Hacker
Ja, es ist tatsächlich so nicht möglich.
Konkret geht es darum, aus plain Text Markdown zu erzeugen.

In Markdown gibt es keine geschützten Leerzeichen, wenn aber (in diesem Fall mit Pandoc) in Html konvertiert wird, können selbstverständlich mit ' ' Leerzeichen erzwungen werden.

Nun kann ich allerdings nicht pauschal alle Leerzeichen durch ' ' ersetzen, denn zB werden echte Leerzeichen in Markdown zum Erzwingen von Zeilenumbrüchen und zum Erzeugen von Listen und Codeblocks benötigt. Für diesen Fall kann ich aber zunächst Leerzeichen maskieren.

Als Beispiel wird '- Text' eine Liste erzeugen, '- Text' aber nicht.

Also ersetzte ich erst zu '-^Text', danach alle verbleibenen Leerzeichen zu ' '
und danach 's/\^/ /g'. Das ist also nicht das Problem.

Aber 'normale' Leerzeichen zwischen einzelnen Worten durch ' ' zu ersetzen, sieht in Markdown so richtig sch... aus.

Also müsste ich erreichen, dass nur dann Leerzeichen durch ' ' ersetzt werden, wenn es mehr als eins ist. Also 's/ \+/  /g'. Dann aber bekomme ich natürlich immer nur ein ' ' egal wieviele Leerzeichen ich ersetzt haben will.

Oder auch hier maskieren mit 's/\(^ \) \(^ \)/\1^\2/g'
Aber welch ein Aufwand.

Und genau da liegt der Hase im Pfeffer.
 
A

Anonymous

Gast
Nimm mal das als Ansatz, vielleicht kommst du damit weiter.
Beruht auf einer Schleifenlösung

Code:
# echo "ab abbb abbbb fbbb abbbbb" | sed ':a;s/\(ac*\)b/\1c/g;t a;'
ac accc acccc fbbb accccc
robi
 
Oben