Thread RegEx mit bedingter Ersetzung (11 answers)
Opened by micha2 at 2023-07-09 13:44

Linuxer
 2023-07-10 23:52
#195136 #195136
User since
2006-01-27
3875 Artikel
HausmeisterIn

user image
Willkommen im Forum, Willkommen bei Perl und Willkommen bei den Regex.

Ich kann mich den Empfehlungen der anderen nur anschließen. Lass ab vom Perl-Einzeiler; der macht das Leben nur unnötig schwer.
"In der Kürze..." ist nicht immer das korrekte Konzept. Vor allem, weil sich ja Perl Skripte schreiben lassen, die dann wie ein Programm verwendet werden können...

Code: (dl )
1
2
3
4
5
6
7
8
# z.b: perlskript liegt unter $HOME/bin und ist ausführbar markiert
# und PATH enthält $HOME/bin als Suchpfad

# mit überflüssigem cat
cat foo.html | perlskript | tee result.html

# ohne
perlskript foo.html | tee result.html


Innerhalb vom perlskript kann man dann sauberen, strukturierten, kommentierten Code schreiben; den man auch nach Wochen, Monaten oder Jahren noch verstehen kann. Bei Einzeilern ist das oft so eine Sache...

Wie eingangs geschrieben, auch ich rate Dir vom Einzeiler ab und rate Dir zu einem vollen Skript; GwenDragon hat Dir ja schon einen möglichen Entwurf aufgezeigt.



Ich möchte aber noch etwas zur Deinem letzten Code-Beispiel schreiben (sieh es als Ergänzung zur hajs Antwort; er hat das auch schon beschrieben, nur mit weniger Worten):

Code: (dl )
1
2
echo 'abbc' | perl -pe "s,b,{if (1) {print \"<\"}; print \">\"; if (0) {print \"y\"}},ge" # <><>a00c
echo 'abbc' | perl -pe "s,b,{if (1) {\"<\"}; \">\"; if (0) {\"y\"}},ge" # a00c


Dein Problem liegt meiner Meinung nach hier im logischen Verständnis, was da passiert:

Im ersten Fall:
Die print-Anweisungen werden bereits ausgeführt, während die Regex noch abgearbeitet wird. Daher landen ihre Ausgaben "<" und ">" vor der Ausgabe des überarbeiteten String.
Beachte: Der Rückgabewert des print ist nicht gleichbedeutend mit dem, was es ausgibt; das muss unterschieden werden!
Was print ausgibt, landet nicht in der Rückgabe und kann daher so auch nicht im Ersetzungsteil genutzt werden.

Zusätzlich gilt, dass Du den Ersetzungsteil als Subroutinenaufruf betrachten solltest.
Eine Subroutine in Perl liefert nur das zurück, was sie entweder per return zurückliefern soll, oder eben was das letzte Kommando innerhalb der Subroutine zurückgeliefert hat.
Und es gibt quasi nur einen "Point of Return". Entweder dediziert per return (mit Wert oder "ohne") irgendwo in der Routine, oder eben am Ende mit der Rückgabe des letzten Kommandos.

Das erste print wird ausgegeben und seine Rückgabe verworfen, weil es nicht das letzte Kommando war.
Das zweite print wird ausgegeben und seine Rückgabe verworfen, weil es auch nicht das letzte Kommando war.
Das letzte Kommando ist das if(0), das als "Nicht wahr" ausgewertet wird, womit das dritte print gar nicht ausgeführt wird.
Als Rückgabe wird das Ergebnis des if() genommen, das ist hier die 0.


Im zweiten Fall gibt es keine prints, aber der Rest bleibt im Prinzip gleich.

"<" und ">" stehen ohne Kontext rum. Kein Print, keine Zuweisung und sie sind auch nicht das letzte Kommando in der Ersetzung. Absolut leerer Kontext...
Damit passiert hier:
Die Rückgabe der "<" verfällt, weil da kommt ja noch was.
Die Rückgabe der ">" verfällt ebenso, weil danach kommt ja immer noch was.
Das if(0) gibt 0 zurück, diese wird dann als Rückgabewert genommen und bildet somit den Ersetzungsstring. Damit werden die "b" wieder zu "0".

Beachte: Gerade beim "Entwickeln" ist es sinnvoll, die Warnungen einzuschalten, damit Perl auf mögliche Probleme hinweisen kann:

Code: (dl )
1
2
3
4
$ echo 'abbc' | perl -wpe 's,b,{if (1) {"<"}; ">"; if (0) {"y"}},ge'
Useless use of a constant ("<") in void context at -e line 1.
Useless use of a constant (">") in void context at -e line 1.
a00c


Mit Warnungen erzählt Dir Perl sogar, dass da was unsinniges passiert.

Du müsstest eine Hilfsvariable verwenden, um die verschiedenen Fälle einzufangen, und diese ganz am Schluß "zurückgeben" lassen.

Code: (dl )
1
2
echo 'abbc' | perl -Mstrict -wpe 's,b,my $replacement=""; if (1) {$replacement.="<"}; $replacement.=">"; if (0) {$replacement.="y"} $replacement,ge'
a<><>c

Die Variable $replacement wird hier als letztes als Rückgabewert benutzt; bzw. ihr Inhalt.

Das mag so funktionieren.
Aber das ist doch jetzt schon nicht mehr gut zu lesen. Wenn dann Deine Suchmuster und Bedingungen auch noch komplexer werden, dann wird das immer schlimmer. Hier solltest Du echt dringend auf ein Skript umschwenken.
meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!

View full thread RegEx mit bedingter Ersetzung