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

[solved] Perl: substitution klappt nicht

Moin, Moin,

ich versuche derzeit ein Skript zu schreiben das mir die möglichen Auflösungen in XF86Config-4 ändert. Aufruf erfolgt über './PROGRAMMNAME AUFLÖSUNG'

Code:
#!/usr/bin/perl -w

open RES, "XF86Config-4"
  or die "$!";


while ($test = <RES>) {
  chomp ($test);
  $^I = ".bak";
  if ($test =~ /Modes\s\s/) {
    my $ersetzung = "\"$ARGV[0]\" $'";
    $test = "$&$ersetzung";
    s/$'/$ersetzung/g;
    print;
  }
}
close RES;
funktioniert allerdings nicht so wie erwartet. Ich erhalte
Use of uninitialized value in substitution (s///) at ./res_conf line 13, <RES> line 89.
Ändere ich zum Testen den code in
Code:
#!/usr/bin/perl -w

open RES, "XF86Config-4"
  or die "$!";


while ($test = <RES>) {
  chomp ($test);
  if ($test =~ /Modes\s\s/) {
    my $ersetzung = "\"$ARGV[0]\" $'";
    $test = "$&$ersetzung";
    print"$test\n";
  }
}
close RES;
so scheint er zu verstehen was ich von ihm will und er gibt mir auf der Ausgabe die richtige neu Zeile aus. Mir ist bei der substitution nicht klar, welche Variable er nicht lesen kann und warum.
 
s/$'/$ersetzung/g;

So wie diese Zeile notiert ist, will der Perl-Interpreter einen Inhalt in Perls Standard-Skalaren Variable $_ ersetzen, und die ist in deinem Skript nicht gefüllt (-> "uninitialized value").

Die Notation bei einer Subsitution sieht grundsätzlich so aus:
Code:
$zu_ersetzende_variable =~ s/[suchmuster]/[ersetzung]/[Modifikator(en)]

Lässt du den Teil $zu_ersetzende_variable =~ vorne weg, wird $zu_ersetzende_variable vom Perl-Interpreter ohne Meldung o.ä. durch $_ ausgetauscht - jetzt wird ausgeführt:

Code:
$_ =~ s/[suchmuster]/[ersetzung]/[Modifikator(en)]

Und $_ ist hier eben leer.

Mit ist nicht ganz klar, was bzw. wo du mit der Substitution tatsächlich austauschen willst. Wenn du den gewünschten neuen Inhalt in $test (Zeile 12) bereits drin hast, brauchst du doch nichts mehr ersetzen? Stattdessen müsstest du alle Zeilen am Ende deiner while-Schleife in einem Array sammeln oder einen großen Skalar draus basteln und das Ganze dann wieder in die Datei zurückschreiben.

Gruß
ginka
 
Moin ginka,

dein Tip bringt mich jetzt ein Stückchen weiter, aber immer noch nicht zum Ziel.
Ich suche in der XF86Config-4 nach der Zeile:
Modes \t\t "1024x768" "800x600" "640x480"

und wollte nun auf 'Modes \t\t' matchen um alles was danach kommt durch die dem Skript mitgegebene Auflösung und den danach kommenden Rest ("1024x768"...) zu ersetzen.

Derzeit neuester Stand ist:
Code:
#!/usr/bin/perl -w

open RES, "+<XF86Config-4"
  or die "$!";


while ($test = <RES>) {
  chomp ($test);
#  $^I = ".bak";
  if ($test =~ /Modes\s\s/) {
    my $ersetzung = "\"$ARGV[0]\" $'";
    $test =~ s/$'/$ersetzung/g;
    print RES "$test";
  }
}
close RES;
Trotzdem funktioniert die Ersetzung nicht da ich jetzt Zeilen der Art
Modes "1024x768" "800x600" "640x480"
Modes "1280x1024" "1024x768" "800x600" "640x480"odes "1024x768" "800x600" "640x480"
erhalte. So langsam weiß ich nicht mehr wo der Gedankenfehler bei mir liegt. Wenn ich mir nämlich $test auf die Konsole ausgeben lasse, stimmt die Ausgabe, aber nicht beim zurückschreiben in die Datei. Vom Anlegen einer Backupdatei will ich gar nicht erst anfangen.
Leider ist mir das Buch 'Einführung in Perl' keine grolße Hilfe, da solche Feinheiten wie das was Du mir schriebst nicht eingegangen wird.
 
Aus meiner Erfahrung ist es ziemlich haarig, eine Datei via "+>" zum Lesen und Schreiben gleichzeitig zu öffnen. Ich bin inzwischen dazu übergegangen, zuerst zu lesen & zu modifizieren, und dann die Datei mit dem neuen Inhalt komplett neu anzulegen (d.h. die alte zu überschreiben).

In deinem Beispiel könnte das etwa so aussehen:
Code:
#!/usr/bin/perl -w

open RES, "<XF86Config-4"
  or die "$!";

$content = '';
$content .= $_ while (<RES>);
close RES;

$content =~ s/(Modes\t{2})("\d+x\d+")/$1"$ARGV[0]" $2/;

open RES, ">XF86Config-4"
  or die "$!";
print RES $content;
close RES;

Dieser Code funktioniert nur dann zuverlässig, wenn es in der Datei XF86Config-4 nur eine Zeile mit dem Suchmuster(-Beispiel) "Modes\t\t1024x768" gibt - sonst basteln wir den regulären Ausdruck halt nochmal um.

Ganz klar ist mir die Anzahl deiner Ersetzungen nicht - du schreibst du suchst nach der Zeile ... (=Einzahl), in deinem Code hast du jedoch den Modifikator 'g' für globales Suchen & Ersetzen gesetzt. Wieviele Ersetzungen soll es geben (hab' hier an meinem Büro-Arbeitsplatz leider grad keinen Zugriff auf eine "echte" XF86Config-4)?

Wenn der passende reguläre Ausdruck einmal gefunden ist, kannst du dir die Angelegenheit mit einem Einzeiler auf der Konsole noch einfacher machen:

Ohne Sicherungsdatei:
Code:
perl -p -i -e 's/(Modes\t{2})("\d+x\d+")/$1"1280x1024" $2/' XF86Config-4

Mit Sicherungsdatei:
Code:
perl -p -i.bak -e 's/(Modes\t{2})("\d+x\d+")/$1"1280x1024" $2/' XF86Config-4

Den Einzeiler kannst du natürlich auch in ein Shell-Skript packen und dieses dann wieder mit der neu einzutragenden Auflösung als Argument aufrufen.

Vielleicht hilft dir das weiter...

Gruß
ginka
 
Moin ginka,

besten Dank, Du hast mich auf den richtigen Weg gebracht.

Code:
#!/usr/bin/perl -w
use strict;
open ( RES, "<./xorg.conf")
  or die "$!";

my @daten =<RES>;
foreach my $daten (@daten){
    if ($daten =~ /Modes /){
	my $ersetzung = "\"$ARGV[0]\" $'";
	$daten =~ s/$'/$ersetzung/;
    }
}
close RES;
open( RES, ">xorg.conf")
  or die "$!";
foreach my $daten (@daten){
    print RES "$daten";
}
close RES;

Ist zwar jetzt für eine xorg.conf Datei, da ich zu Hause xorg verwende und den Code neuschreiben mußte, aber jetzt funktioniert das Ganze so wie ich mir das vorgestellt habe. Für jede Farbtiefe gibt es eine 'Modes'-Zeile in der die möglichen Auflösungen drin stehen und die wollte ich alle jeweils um einen Wert, der per Programmaufruf mitgegeben werden sollte, erweitern. Ich habe heute wieder eine ganze Menge gelernt.
 
Oben