linux-l: Perl mal wieder...

Jens Dreger jens.dreger at physik.fu-berlin.de
Do Feb 10 00:11:01 CET 2000


On Tue, Feb 08, 2000 at 09:14:58PM +0100, Oliver Bandel wrote:
> Hi!
> 
> On Mon, 7 Feb 2000, Carsten Wartmann wrote:
> 
> > Hi,
> > 
> > ich kann Perl immer noch nicht so richtig leiden,
> 
> Wird sich bestimmt bald ändern... ...hoffe ich jedenfalls,
> ist nämlich richtig schnuckelig. :-)
 
Allerdings !
 
Wohin jetzt mit dem "Hallo Carsten" ? Ich tu's mal in die naechste
Zeile:
Hallo Carsten !

Nachdem Du ja meinen Einzeiler nicht wolltest (*schnueftz*), haette
ich Dir auch ungefaehr so ein Skript geschickt, wie es Oliver getan
hat, nur hab' ich's irgendwie vergessen. Trotzdem sehe ich ein, dass
es irgendwie schlecht verstaendlich ist, zumindest fuer einen Perl-
Leihen. Ich werde es also mal fuer Dich "hochkochen", also so viel
unnoetiges Zeug dazuschreiben, dass man es eigentlich verstehen
sollte, ohne perl zu kennen. Das Problem an Perl ist naemlich, dass
man alles weglassen kann, Perl nimmt dann schon das richtige... 

> #!/usr/bin/perl # Wenn's denn Dein Pfad ist...
> 
> $alt = 'layout.inc';
> $neu = 'print.inc';
> $key = "\#include \"$alt\"";
> 
> while(<>)
> {
>   s/$alt/$neu/ if /$key/o;
>   print;
> }

Du moechtest also unbedingt eine Datei oeffnen und in eine Datei
schreiben. Gut, machen wir:

--8<---------------
#!/usr/bin/perl -w    # warnings koennen nicht schaden

open( ALT, "<"."alte_datei.txt" );   # man perlfunc -> open
open( NEU, ">"."neue_datei.txt" );

# ALT und NEU sind jetzt Filehandles fuer die entsprechenden Dateien
# jetzt von Datei ALT eine Zeile lesen und in Datei NEU schreiben

$line = <ALT>;		# man perlop -> I/O Ops, perlfunc -> readline
print NEU $line;	# man perlfunc -> print    

# statt <FILEHANDLE> kannst Du auch readline(FILEHANDLE) schreiben, macht
# aber _niemand_. Da wir aber eigentlich die ganze Datei kopieren
# wollten, ist es besser gleich sowas zu schreiben:

while($line = <ALT>) {	 # man perlsyn -> while ;-)
    # irgendwelche Tests mit der Zeile
    print NEU $line;
}

# Das while($line = <ALT>) funktioniert, weil <ALT> ein undefined
# zurueckgibt, wenn es an das Ende der Datei prallt. Dann ist die
# condition nicht mehr erfuellt, und die while-Schleife beendet.
# Jetzt wollen wir noch was ersetzen. Also:

while($line = <ALT>) {
    # wenn in der richtigen Zeile, sprich $line enthaelt den
    # gesuchten pattern:
    if($line =~ /^#include "layout.inc"/) {
         # dann ersetzen
         $line =~ s/layout.inc/print.inc/;
    }

    # in jedem Fall in neue Datei schreiben
    print NEU $line;
}

# bei den pattern muss man sich natuerlich etwas mehr Muehe geben, als
# ich das jetzt getan habe. z.B. muesste es besser "layout\.inc"
# heissen, denn "." steht fuer "beliebiger character (man perlre)
# jetzt noch Dateien schliessen, und fertich:

close(NEU);
close(ALT);

# Jetzt fragst Du Dich wahrscheinlich, wieso das fast das gleiche ist wie
#   perl -pe 's/^#include "layout.inc"/#include "print.inc"/'

Du kannst statt      while($line = <ALT>)
einfach schreiben    while(<ALT>)

Die Zeile ist jetzt in der allgegenwaertigen $_ Variable gespeichert,
die immer gemeint ist, wenn man nix schreibt. Dann kannst Du
natuerlich statt     print NEU $line
schreiben            print NEU

Natuerlich waere     print NEU $_
auch gegangen, aber wozu ? Wir sind ja tippfaul.

Als naechstes koennen wir statt <ALT> auch nur <> schreiben. Aber
welche Datei wird jetzt geoeffnet ? Bei <> nimmt Perl der Reihe nach
die Dateinamen, die auf der Kommandozeile angegeben wurden, oder
STDIN, wenn die Kommandozeile leer ist (sprich: @ARGV ist leer).

Dadurch sind wir auch das open( ALT...) los. Wenn Du bereit bist,
nicht in eine Datei sondern nach STDOUT zu schreiben (und dann in eine
Datei mittels > umzuleiten), koennen wir uns auch das NEU sparen, denn
einfach nur "print" heisst fuer Perl, auf den zuletzt selektierten
Filehandle schreiben (man perlfunc -> select). Das ist, wenn man
nichts geaendert hat, STDOUT.

Also, open und close sind nun weg, $line ist durch $_ ersetzt:

---------8<--------------
#!/usr/bin/perl -w
while(<>) {
    if($_ =~ /^#include "layout.inc"/) {
         $_ =~ s/layout.inc/print.inc/;
    }
    print;
}
---------------8<-------------

Natuerlich kann man die $_ wieder weglassen. Wenn ein /pattern/ oder
ein s/alt/neu/ im freien Raum stehen, wirken sie auf $_.

---------8<------------
#!/usr/bin/perl -w
while(<>) {
    if(/^#include "layout.inc"/) { s/layout.inc/print.inc/; }
    print;
}
-------------8<---------

Dann kann man das if und das s// noch zusammenziehen, wenn man
will. Und da "-p" genau solch ein Konstrukt automatisch erzeugt (man
perfrun -> -p) ist man bei:

---------8<-----------
#!/usr/bin/perl -w -p 

s/^#include "layout.inc"/#include "print.inc"/;
-----------8<-------------

oder
	perl -pe 's/^#include "layout.inc"/#include "print.inc"/'

Durch ein -i.bak kann man perl dazu veranlassen, gleich die Datei zu
modifizieren und eine backup-Datei mit der Endung .bak anzulegen.

Wie gesagt, muss man sich noch gedanken ueber die pattern machen. Sie
sollten natuerlich nur die Zeile matchen, die auch gemeint ist. Durch
ein /pattern/o (also das 'o') kann man perl mitteilen, dass der
pattern sich nicht aendert, es also reicht, ihn nur ein einziges mal
zu kompilieren (das tut perl naemlich mit patterns). Ferner sollte man
natuerlich immer nach eine open pruefen, ob's auch geklappt hat (sonst
kommt der boese Watz und frisst das Skript), aber das is' ja klar ;-)

Hoffe, ich konnte Dir Perl etwas naeher bringen. Sonst frag einfach
nach...

Gruss,

Jens.

PS: Skripts sind nicht getestet, funtktionieren also wahrscheinlich
    nicht.



Mehr Informationen über die Mailingliste linux-l