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

[gelöst]Kleine Dateimodifikation aus Shell-Skript (mit Perl)

utopos

Member
ÄNDERUNG: Musste Überschrift leicht modifizieren (von Hand ;) ), um das "Gelöst"-Kennzeichen zu setzten, da die größtmögliche Länge offenbar erreicht war. Ab der nächsten Zeile steht die Originalanfrage. Utopos, 8.5.13

Hallo zusammen,


in einem bash-Skript möchte ich Folgendes umsetzen:

1. Öffnen einer gegebenen Datei,
2. Lesen der Datei, bis ein ebenfalls gegebener Suchstring gefunden wird,
3. Ersetzen der Zeile mit dem Treffer durch einen festen neuen String.

Wir dürfen dabei davon ausgehen, dass der Suchstring höchstens einmal in der Datei vorkommt;
das Abfangen bei null Treffern traue ich mir ggf. auch noch zu. Es geht also hier v.a. um den Fall mit einem Treffer.

Das Suchen ist ohne Weiteres mit grep möglich. Da ich aber auch schreiben möchte, scheint das ein Fall für perl zu sein.

Der folgende Befehl ist getestet und tut, was ich will:

Code:
touch tmpDatei
rm tmpDatei
perl -ne 'if (/suchString/) {print "Zeile ersetzt"} else {print $_}' suchDatei >> tmpDatei
rm suchDatei
mv tmpDatei suchDatei


Das Herumwurschteln mit den Dateien wäre dabei noch zu ertragen. Was mir aber gar nicht gefällt, ist, dass das Skript extrem ineffizient ist: Es muss zwangsläufig alle Zeilen der Datei lesen und wieder schreiben, auch wenn die einzige Änderung bereits in einer der ersten Zeilen erfolgt ist.

Gibt es hierfür einen einfachen Weg, in einem solchen Perl-Einzeiler direkt in die Datei zu schreiben?


Ich freue mich auf Eure Vorschläge.
 
utopos schrieb:
in einem bash-Skript möchte ich Folgendes umsetzen:

1. Öffnen einer gegebenen Datei,
2. Lesen der Datei, bis ein ebenfalls gegebener Suchstring gefunden wird,
3. Ersetzen der Zeile mit dem Treffer durch einen festen neuen String.
Wenn Du diesen Code benutzt, wie soll da ein Erfolg gehen?:

utopos schrieb:
Code:
touch tmpDatei
rm tmpDatei
perl -ne 'if (/suchString/) {print "Zeile ersetzt"} else {print $_}' suchDatei >> tmpDatei
rm suchDatei
mv tmpDatei suchDatei
Erst erstellst Du eine tmpDatei und in der folgenden Zeile, also direkt danach löscht Du diese wieder?!

Wenn Du also schreibst:

utopos schrieb:
Wir dürfen dabei davon ausgehen, dass der Suchstring höchstens einmal in der Datei vorkommt;
Ist die Sache doch einfach:
Code:
#!/bin/bash
grep -v "suchString" suchDatei > tmpDatei
rm suchDatei
mv tmpDatei suchDatei

grep schaut also ob es den suchString gibt und filtert ihn heraus, was übrig bleibt wird in die tmpDatei geschrieben.

utopos schrieb:
das Abfangen bei null Treffern traue ich mir ggf. auch noch zu. Es geht also hier v.a. um den Fall mit einem Treffer.
Wenn der suchString nicht vorkommt bleibt die Datei unberührt, es gibt ja nichts zu tun.
Wenn der suchString mehrfach vorkommt werden eben alle Zeilen in denen das der Fall ist raus geschnitten.

utopos schrieb:
Das Suchen ist ohne Weiteres mit grep möglich. Da ich aber auch schreiben möchte, scheint das ein Fall für perl zu sein.
Gibt es hierfür einen einfachen Weg, in einem solchen Perl-Einzeiler direkt in die Datei zu schreiben?.
Bei Deinem Szenario sehe ich nicht wo die Vorteile von perl liegen sollen, ich kenne mich allerdings auch nicht mit perl aus.
Es gibt sicher noch viele andere Wege, aber es müssen ja alle Zeilen bis zur Fundstelle durchsucht werden, sicher lässt es sich so einrichten das nur die erste Fundstelle ersetzt wird und der Suchvorgang danach abgebrochen wird.
Zum Beispiel wenn man sed hinzunimmt:
Code:
#!/bin/bash
sed -n '/suchString///\1/p' suchDatei >tmpDatei
rm suchDatei
mv tmpDatei suchDatei
sed kann ja auch suchen und versteht es nach der ersten Fundstelle aufzuhören.

Lieben Gruß aus Hessen
 
OP
U

utopos

Member
Herz-von-Hessen schrieb:
Wenn Du diesen Code benutzt, wie soll da ein Erfolg gehen?:
Erst erstellst Du eine tmpDatei und in der folgenden Zeile, also direkt danach löscht Du diese wieder?!

Der perl-Befehl schreibt ja nach tmpDatei. Das Anfassen und Löschen kann ich mir tatsächlich schenken, wenn ich

Code:
touch tmpDatei
rm tmpDatei
perl -ne 'if (/suchString/) {print "Zeile ersetzt"} else {print $_}' suchDatei >> tmpDatei

durch

Code:
perl -ne 'if (/suchString/) {print "Zeile ersetzt"} else {print $_}' suchDatei > tmpDatei

ersetze, das war mir eben noch nicht klar. In beiden Fällen aber tut das Skript das richtige.

_________________________________________________________________________


Herz-von-Hessen schrieb:
[...]
Ist die Sache doch einfach:
Code:
#!/bin/bash
grep -v "suchString" suchDatei > tmpDatei
rm suchDatei
mv tmpDatei suchDatei

Aha! Die Option grep -v kannte ich nicht. Trotzdem führt das nicht zum gewünschten Ziel:
Die Zeile mit dem Treffer will ich ja durch eine andere Zeile ersetzen, nicht einfach streichen.
 

abgdf

Guru
utopos schrieb:
Das Herumwurschteln mit den Dateien wäre dabei noch zu ertragen. Was mir aber gar nicht gefällt, ist, dass das Skript extrem ineffizient ist: Es muss zwangsläufig alle Zeilen der Datei lesen und wieder schreiben, auch wenn die einzige Änderung bereits in einer der ersten Zeilen erfolgt ist.
Ich schätze, das muß man immer, egal in welcher Sprache.

Man könnte sonst höchstens versuchen, die einzelnen Bytes in der Datei zu finden und zu verändern, aber dann bekommt man Probleme, wenn der Suchstring nicht dieselbe Länge hat wie der, mit dem man ihn ersetzen will.
Also, Zeile ersetzen und dann aber alles neu schreiben ist schon notwendig.

Ansonsten: Perl hat hier tatsächlich nicht viele Vorteile gegenüber sed. Perl hatte ja einst die Ersetzungsroutinen von sed übernommen. Ist also im wesentlichen das gleiche.
Ich persönlich würde auch davon abraten, Perl zuerst über die Einzeiler kennenzulernen. Die sind ja sehr stark verkürzt, setzen also eigentlich eine Menge Hintergrundwissen voraus. Dieses sollte man sich wohl zuerst aneignen.
Eine Datei öffnen, darin suchen und darin Veränderungen vornehmen, würde ich dann ganz in Perl schreiben, weil man da insgesamt einfach mehr Kontrolle hat, man leistungsfähige Arrays zur Verfügung hat und es da auch keine Probleme mit etwaigen Stringsubstitutionen oder gar Subshells wie in der bash gibt. Bis zu einem gewissen Grad finde ich Perl sogar leichter als bash, weil das Verhalten aus den genannten Gründen einfach besser vorhersagbar ist.
bash hat dagegen seine Berechtigung bei der Arbeit mit Dateien (kopieren, verschieben, Verzeichnisse erstellen, usw.). Das geht natürlich auch in Perl, aber da ist es umgekehrt in bash einfacher und schneller (zu schreiben).
 
A

Anonymous

Gast
Wenn eine Datei geändert werden muss, dann muss so oder so die ganze Datei gelesen und wieder neu geschrieben werden. Es geht zwar auch das nicht wirklich jede Zeile gelesen werden muss, aber nur wenn du die Datei im rw-Modus öffnest. Wäre aber weder schnell und effizient mit Perl zu programmieren, noch machbar mit den meisten Scriptsprache, dazu müsstest du einen Ebene tiefer also zB nach C aber dort bekommst du als nicht Geübter schnell Probleme, da es dort keine "fertigen" einfach zu benutzende Textbearbeitungsfunktionen gibt..

Dein Problem ist einfach und vollkommen unkompliziert mit sed zu lösen.
Code:
sed -i 's/SUCHSTRING/ERSETZUNG/' dateiname
was
3. Ersetzen der Zeile mit dem Treffer durch einen festen neuen String.
genau bedeuten soll, wird aus deiner Erklärung nicht ganz klar, wenn die ganze Zeile in der der Suchstring steht, ersetzt werden soll dann eben so hier.
Code:
sed -i 's/^.*SUCHSTRING.*$/ERSETZUNG/' dateiname

bevor du jedoch "sed -i ....." auf deine hyperwichtigen Orginaldateien losläst, erst mal ohne "-i" die sed-Zeile an ein paar Dateien testen.

robi
 

abgdf

Guru
robi schrieb:
Code:
sed -i 's/SUCHSTRING/ERSETZUNG/' dateiname
Das ersetzt aber jedes Vorkommen von SUCHSTRING in der Datei und nicht nur den ersten.
In Perl gibt's glaube ich auch die Möglichkeit, schon in der RegEx festzulegen, wie oft ersetzt werden soll. Hab' die Syntax aber gerade nicht im Kopf.
 
A

Anonymous

Gast
abgdf schrieb:
robi schrieb:
Code:
sed -i 's/SUCHSTRING/ERSETZUNG/' dateiname
Das ersetzt aber jedes Vorkommen von SUCHSTRING in der Datei und nicht nur den ersten.
Na ich denke da ist nur einmal in der Datei drin ?
utopos schrieb:
Wir dürfen dabei davon ausgehen, dass der Suchstring höchstens einmal in der Datei vorkommt;

es geht mit sed auch anders, das nach dem ersten Vorkommen des Suchstrings mit dem Suche/Ersetzten aufgehört werden kann, das wird aber hier ein kleines bisschen komplizierter, da ja der Rest auch noch gelesen und geschrieben werden muss.
Code:
sed -i '0,/SUCHSTRING/{s/SUCHSTRING/ERSETZUNG/;b};{}' dateiname
ich erspare mir hier mal die ausführliche Erklärung was dort genau passiert, will sowieso niemand wissen. ;)

robi
 
Hallo

ich hatte doch in meinem Eingangsposting schon geschrieben:
Code:
sed -n '/suchString///\1/p' suchDatei >tmpDatei
Damit wird nur die erste Stelle ersetzt.

Lieben Gruß aus Hessen
 

abgdf

Guru
Herz-von-Hessen schrieb:
Hallo

ich hatte doch in meinem Eingangsposting schon geschrieben:
Code:
sed -n '/suchString///\1/p' suchDatei >tmpDatei
Damit wird nur die erste Stelle ersetzt.

Lieben Gruß aus Hessen
Oh, sorry, hatte ich wohl übersehen. :eek:ps: War keine böse Absicht.
 
A

Anonymous

Gast
Herz-von-Hessen schrieb:
ich hatte doch in meinem Eingangsposting schon geschrieben:
Code:
sed -n '/suchString///\1/p' suchDatei >tmpDatei
Damit wird nur die erste Stelle ersetzt.
oder der Fehler ausgegeben "Unbekannter Befehl: '/' ;)

Keine Ahnung was du mit dieser Zeile machen willst, die ist leider fehlerhaft.

robi
 
Hallo robi,

robi schrieb:
oder der Fehler ausgegeben "Unbekannter Befehl: '/' ;)

Keine Ahnung was du mit dieser Zeile machen willst, die ist leider fehlerhaft
Zugegeben, ich habe das so auch nur ergoogelt und in Ermangelung an Zeit nicht selbst getestet. :eek:ps:
Da wäre dann ein tieferer Blick in die Manpage von sed hilfreich für den Themenstarter.

Lieben Gruß aus Hessen
 
utopos schrieb:
Das Suchen ist ohne Weiteres mit grep möglich. Da ich aber auch schreiben möchte, scheint das ein Fall für perl zu sein.
dann nimm doch sed ...ungefähr so:
Code:
sed -i 's/.*bla=.*/bla=blub/g' datei
vorsicht! -i ändert die datei sofort ohne eine sicherung anzulegen.
 

abgdf

Guru
Reiner Text schrieb:
Code:
sed -i 's/.*bla=.*/bla=blub/g' datei
Ändert das jedes Vorkommen des Suchtextes in der Datei oder nur das erste Vorkommen (was der Ausgangsposter möchte)?

("s///g" deutet ja darauf hin, daß das sogar mehrmals pro Zeile geändert würde ...)
 
OP
U

utopos

Member
Vielen Dank Euch allen für die Beiträge!

Inzwischen haben sich ja einige verschiedene Diskussionsstränge herauskristallisiert:


1. Bewegliche Lettern.


utopos schrieb:
Was mir aber gar nicht gefällt, ist, dass das Skript extrem ineffizient ist: Es muss zwangsläufig alle Zeilen der Datei lesen und wieder schreiben, auch wenn die einzige Änderung bereits in einer der ersten Zeilen erfolgt ist.

abgdf schrieb:
Ich schätze, das muß man immer, egal in welcher Sprache.

robi schrieb:
Wenn eine Datei geändert werden muss, dann muss so oder so die ganze Datei gelesen und wieder neu geschrieben werden.


Aha! Dann habe ich hier einen Eindruck von einer sehr maschinennahen Tatsache bekommen:
Ich hatte bisher immer gedacht, wenn ich mit einem Editor - egal ob pico oder kate oder was auch immer - eine (große) Textdatei öffne, würde nur so viel davon gelesen und geladen, wie gerade angezeigt werden kann und muss. Wenn ich auf dieser ersten Bildschirmseite, so dachte ich weiter, etwas ändere und die Datei speichere, dann würde auch nur für den betreffenden Abschnitt etwas im Speicher geändert. Das setzt natürlich voraus, dass die Textdatei intern als eine Art verkettete Liste von Zeilen abgespeichert ist (die ihrerseits wieder eine einfache Sorte von Listen darstellen). Aber anscheinend ist das anders?


robi schrieb:
Es geht zwar auch das nicht wirklich jede Zeile gelesen werden muss, aber nur wenn du die Datei im rw-Modus öffnest. Wäre aber weder schnell und effizient mit Perl zu programmieren, noch machbar mit den meisten Scriptsprache, dazu müsstest du einen Ebene tiefer also zB nach C aber dort bekommst du als nicht Geübter schnell Probleme, da es dort keine "fertigen" einfach zu benutzende Textbearbeitungsfunktionen gibt..

Richtig, perl -e öffnet nur zum Lesen. Gibt es keine Variante des Perl-Aufrufs, bei der die Argument-Datei automatisch auch zum Schreiben geöffnet wird?

Interessant finde ich, dass Du sagst, das sei mit C möglich; das spräche ja gegen obige Erkenntnis. Bei C(++) betrachte ich mich doch als Geübter; auch Dateizugriffe sind mir geläufig, nur mit Zugriff auf Textdateien hatte ich bisher nichts zu tun.




1. sed-isvakanz.

abgdf schrieb:
Ansonsten: Perl hat hier tatsächlich nicht viele Vorteile gegenüber sed.

robi schrieb:
Dein Problem ist einfach und vollkommen unkompliziert mit sed zu lösen.

Perl hat für mich den überragenden Vorteil, dass ich mich jetzt dort eingelesen und ein-probiert habe ... ;)

Ganz ehrlich, ich habe mich lange geweigert, mich mit perl überhaupt zu beschäftigen, weil ich bei Shell-Skripts eben in der Shell bleiben möchte; wenn ich ganze Programme schreiben möchte, kann ich das ja auch in einer mir geläufigen Sprache wie C++ tun.

Nachdem ich aber bei vielen Problemen Hinweise zur Lösung mit Perl-Direktaufrufen (mit -e) bekommen habe, die mir inzwischen auch einleuchten, bin ich doch einigermaßen begeistert davon - insbesondere von der möglichen Verwendung in Pipes.

Im Gegensatz dazu habe ich praktisch gar keine Ahnung von sed. Nun ist perl ja doch eine Turing-mächtige Sprache und, wenn ich das richtig verstehe, sed ausschließlich ein Textverarbeitungswerkzeug. Insofern scheint mir die Beschäftigung mit sed nicht unbedingt sinnvoll für mich zu sein. Eure Diskussion über das sed-Kommando und die dabei zu Tage tretenden Unklarheiten haben mich in dieser Ansicht auch noch etwas bestärkt ...



abgdf schrieb:
Ich persönlich würde auch davon abraten, Perl zuerst über die Einzeiler kennenzulernen. Die sind ja sehr stark verkürzt, setzen also eigentlich eine Menge Hintergrundwissen voraus. Dieses sollte man sich wohl zuerst aneignen.

Ich bilde mir ein zu wissen, was die verkrüppelten Befehle tatsächlich bedeuten - sonst würde ich sie auch nicht verwenden.



3. Des Pudels Kern: Das ursprüngliche Problem.


In Anbetracht der ersten beiden Punkte bin ich dank Eurer Hiweise zum Schluss gekommen, dass das ursprüngliche Programm, nach HerzVonHessens Anregung modifiziert, mehr oder weniger optimal ist (solange ich keine spezielle Dateihandler verwenden möchte):

Code:
perl -ne 'if (/suchString/) {print "Zeile ersetzt"} else {print $_}' suchDatei > tmpDatei
# hier könnte man noch ein paar Tests auf tmpDatei loslassen, wenn man Angst hat
mv tmpDatei suchDatei
rm tmpDatei
 

abgdf

Guru
utopos schrieb:
Ich hatte bisher immer gedacht, wenn ich mit einem Editor - egal ob pico oder kate oder was auch immer - eine (große) Textdatei öffne, würde nur so viel davon gelesen und geladen, wie gerade angezeigt werden kann und muss.
Weiß nicht, aber Microsoft Notepad weigert sich regelmäßig, größere Dateien (gar nicht mal so große) zu öffnen. Spricht dafür, daß alles in den Speicher gelesen wird.
Es ist eher ein Problem des Öffnens und Speicherns und wie die Daten auf der Festplatte liegen. Es gibt die Möglichkeit, mit seek() (siehe "perldoc -f seek") in Dateien hin- und herzuspringen, aber das ist relativ umständlich, macht man eher in binären Dateien.
Wenn Du mit Perl Textdateien einliest, kannst Du die ruhig im Ganzen einlesen, wenn Du weißt, daß Dein Speicher ausreicht.
Wenn nicht, mußt Du die Textdateien zeilenweise einlesen.
Am einfachsten ist es, das Modul "Tie::File" zu verwenden, das kümmert sich automatisch um diese Sachen.
utopos schrieb:
robi schrieb:
Es geht zwar auch das nicht wirklich jede Zeile gelesen werden muss, aber nur wenn du die Datei im rw-Modus öffnest. Wäre aber weder schnell und effizient mit Perl zu programmieren, noch machbar mit den meisten Scriptsprache, dazu müsstest du einen Ebene tiefer also zB nach C
Sorry, aber das stimmt so nicht. Man kann ganz einfach in Perl eine Datei im rw-Modus öffnen:
Code:
open(my $fh, "+<", "dateiname");
Hatte ich in dem Beispiel auch probiert. Leider schreibt er dann immer hinter die Zeile, wenn ich die erst einlese und untersuche. Außerdem scheinen Zeichen überschrieben zu werden, wenn ich mehr schreibe, als ursprünglich in der Datei an der Stelle gewesen ist.
utopos schrieb:
Perl hat für mich den überragenden Vorteil, dass ich mich jetzt dort eingelesen und ein-probiert habe ... ;)
Geht mir auch so, ich nehme daher auch meist lieber "perl -e". Aber ursprünglich ist das schon seds Aufgabe.
Ganz ehrlich, ich habe mich lange geweigert, mich mit perl überhaupt zu beschäftigen, weil ich bei Shell-Skripts eben in der Shell bleiben möchte; wenn ich ganze Programme schreiben möchte, kann ich das ja auch in einer mir geläufigen Sprache wie C++ tun.
C/C++ ist dafür da, um sowas wie sed zu schreiben. Wenn Du in einer Datei was ersetzen willst, willst Du ja nicht jedesmal sed neu schreiben. Du willst Dich nicht um Speicherverwaltung kümmern, Du willst Deine Daten in flexible, dynamische Arrays packen und Du willst mächtige reguläre Ausdrücke zur Textbearbeitung an der Hand haben. Daher Skriptsprachen.
In Anbetracht der ersten beiden Punkte bin ich dank Eurer Hiweise zum Schluss gekommen, dass das ursprüngliche Programm, nach HerzVonHessens Anregung modifiziert, mehr oder weniger optimal ist (solange ich keine spezielle Dateihandler verwenden möchte):
Tja, warum kein Dateihandle?
Code:
#!/usr/bin/perl
use warnings;
use strict;

open(my $fh1, "<", "suchDatei");
my @a = <$fh1>;
foreach my $b (@a) {
    if ($b =~ m/suchString/) {
        $b =~ s/suchString/ersetzenString/;
        last;
    }
}
close $fh1;
open(my $fh2, ">", "suchDatei");
foreach $b (@a) {
    print $fh2 $b;
}
close $fh2;
Ist das nun so schwer?
 
OP
U

utopos

Member
abgdf schrieb:
Tja, warum kein Dateihandle?
Ist das nun so schwer?

Nein, nicht schwer.

Nur gewissermaßen in einem Shell-Skript nach perl -e ein Stilbruch, oder?
Es ist in einer Zeile auch nicht so schön zu lesen ...
 
OP
U

utopos

Member
@abgdf:

Übrigens und unabhängig von den "Händlern", tut Dein Skript auch nicht ganz, was ich vorhatte:

Du ersetzt ja nur Suchstring, nicht die ganze Zeile ...
 

abgdf

Guru
utopos schrieb:
@abgdf:

Übrigens und unabhängig von den "Händlern", tut Dein Skript auch nicht ganz, was ich vorhatte:

Du ersetzt ja nur Suchstring, nicht die ganze Zeile ...
Na ja, von jemandem, dem C++ vertraut ist, kann man schon erwarten, daß er bei Bedarf auch selbständig
Code:
$b =~ s/suchString/ersetzenString/;
zu
Code:
$b = "ersetzenString\n";
ersetzen kann. ;)

Edit: .... wobei das, falls es klappen sollte, an einem sehr speziellen Verhalten von Perl liegt. (Haus-)Aufgabe für utopos: Zu erklären, was genau daran speziell ist. ;)

Edit2: Und Aufgabe2: Es so zu schreiben, daß das spezielle Verhalten nicht benötigt wird.
Wobei die Elemente des Arrays @a über $a[0], $a[1], usw. angesteuert werden können und $#a die "Anzahl der Elemente von @a, minus eins" bezeichnet. for-Schleifen können dabei unter anderem wie in (dem bekannten) C/C++ gebildet werden, also:
Code:
for($i = 0; $i <= $#a; $i++) {}
Viel Spaß!
 
OP
U

utopos

Member
Mit meiner Bemerkung wollte ich verhindern, dass die Nachwelt, die diesen Faden als Referenz bemüht, den letzten Vorschlag missversteht ...

Die Besonderheit, auf die Du ansprichst, ist wohl, dass
- die Zeilen als einzelne Skalare angesprochen werden können (unabhängig von der u.U. veränderten Länge), obschon sie eigentlich selbst wieder Listen sind, und
- die Zeilenvariable $b in der hier verwendeten Schleife "by reference" weitergereicht wird.

Warum Du das anders haben, also hierauf nicht zurückgreifen möchtest, habe ich nicht ganz verstanden.
 

abgdf

Guru
utopos schrieb:
Mit meiner Bemerkung wollte ich verhindern, dass die Nachwelt, die diesen Faden als Referenz bemüht, den letzten Vorschlag missversteht ...

Die Besonderheit, auf die Du ansprichst, ist wohl, dass
- die Zeilen als einzelne Skalare angesprochen werden können (unabhängig von der u.U. veränderten Länge), obschon sie eigentlich selbst wieder Listen sind, und
- die Zeilenvariable $b in der hier verwendeten Schleife "by reference" weitergereicht wird.
Hehe, das ist eine interessante Antwort. Ehrlich gesagt weiß ich nicht genau, ob $b hier eine Referenz ist. Ich glaube, eher nicht.
$b enthält ja jeweils einen String (einen Skalar). Eine Referenz auf einen String würde man in Perl so nehmen:
Code:
my $str = "Hallo";
my $strref = \$str;
Dann müßte man beim Zugriff auch noch dereferenzieren:
Code:
print $$strref . "\n";
So sieht das in der Schleife ja nicht aus.
Aber es stimmt schon: Die Besonderheit ist: Wenn man mit einer foreach-Schleife ein Array durchläuft und innerhalb der Schleife der Schleifenvariable einen neuen Wert zuweist, wird damit in Perl auch zugleich das Array-Element geändert.
Ich finde das sehr ungewöhnlich, in Python z.B. ist das nämlich überhaupt nicht so. Ich finde das auch nicht offensichtlich. Deshalb vermeide ich dieses Verhalten lieber und umschreibe die Konstruktion anders. In dem Beispiel hätte ich also etwa geschrieben:
Code:
my @a = <$fh1>;
for (my $i = 0; $i <= $#a; $i++) {
    if ($a[$i] =~ m/suchString/) {
        $a[$i] = "ersetzenString\n";
        last;
    }
}
Dann wäre für mich deutlicher, was da vorgeht.
Das Problem trat eigentlich schon im Ausgangsbeispiel auf: Schon da hätte das "$b =~ s/../../;" eigentlich nur $b und nicht auch das Element von @a ändern dürfen. Ist mir wegen der RegEx-Konstruktion wohl nicht aufgefallen.
Aber das alles sind im Grunde schon Spezialfragen zu Perl. Diese kann man wohl etwas besser in diesem Perl-Forum diskutieren.
 
Oben