Schrift
[thread]8835[/thread]

implementing 'perl -i': Using a fh rw



<< |< 1 2 >| >> 14 Einträge, 2 Seiten
dukeofnukem
 2007-03-12 19:06
#74981 #74981
User since
2007-01-15
47 Artikel
BenutzerIn
[default_avatar]
Alohá!

Wie benutzt man (oder implementiert falls nicht verfügbar) die Funktionalität von 'perl -i'?

Folgende Situation:

Habe einen Filehandle auf den extrahierte logeinträge geschrieben werden; zum Zeitpunkt des Öffnens kann es sein daß die Datei bereits existiert, möglicherweise aber auch nicht.

Nachdem ich aus der Extraktionsschleife komme, möchte ich meine von volatile data (timestamps, etc.) bereinigte Ergebnisdatei (die über den fh geöffnet ist) nach Duplikaten durchsuchen und diese löschen (mittels hash, ähnlich wie bei Perldoc beschrieben.)

Nun möchte nicht häßlich den fh schließen, die Datei slurpen, dupes removen und wieder burpen, sondern den noch offenen fh benutzen.

Leider kann ich zwar laut Perldoc mittels
Code: (dl )
open("+>> $filename")
rw öffnen, aber nur appending schreiben.

Allzugern würde ich aber
- die Datei z.B. über IO::File öffnen (kann auch klassisches open sein wenns denn sein muß, aber halt append falls schon existent)
- per default appending schreiben (Extraktionsschleife)
- mittels seek($fh, 0, 0); auf Anfang springen ((un)nötig?)
- die Datei mittels @lines = <$fh> slurpen
- das array durch meinen uniquifier jagen
- weiteres (un)nötiges seek($fh, 0, 0) ?
- den Inhalt der Datei komplett durch ein print {$fh} join('', @unique) ersetzen
- Jetzt endlich und einmalig schließen

Wie gehe ich das am besten an?

Muß ich am Ende doch schließen, slurpen, uniquifien, burpen?
Das fände ich häääääääßlich :-)

TMTOWTDI oder?

TIA,

DoN\n\n

<!--EDIT|dukeofnukem|1173719285-->
drum&bass is a state of mind
renee
 2007-03-12 19:24
#74982 #74982
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Nö, verwende einfach CPAN:Tie::File...

Code: (dl )
1
2
3
4
5
6
7
8
use Tie::File;

my $file = '/path/to/file.txt';

tie my @lines,'Tie::File',$file or die $!;
@lines = make_unique(@lines);
untie @lines;
# that's it...
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
betterworld
 2007-03-12 21:08
#74983 #74983
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Die Methode "lesen, seeken, truncaten, schreiben" ist immer etwas gefaehrlich, denn wenn da zwischendurch etwas schiefgeht und das Script aus irgend einem Grund abstuertzt, sitzt man mit einer leeren Datei da und alle Daten sind weg.

Die Funktionalitaet von perl -i kann man auch so erreichen:
Code: (dl )
1
2
3
4
5
6
7
local $_;
local $^I='';
local @ARGV=('datei1.txt', 'datei2.txt');
while (<>) {
  s/\s+//g; # oder irgend etwas anderes lustiges
  print;
}

Das ist allerdings nicht wirklich so elegant wie Tie::File\n\n

<!--EDIT|betterworld|1173726813-->
dukeofnukem
 2007-03-13 13:33
#74984 #74984
User since
2007-01-15
47 Artikel
BenutzerIn
[default_avatar]
Danke für die schnellen und klaren Antworten!

Schön daß man sich so auf die community verlassen kann ;-)

Tie::File ist wirklich ein nettes Modul, leider scheiden externe Module aufgrund verschiedener Probleme aus.

Bzgl. der S^I=`` -Lösung habe ich noch eine Frage:

Wie kann ich erst appenden, dann komplett einlesen, dann komplett überschreiben?
Mangels "echtem" Filehandle funktioniert seek ja wahrscheinlich nicht; auch weiß ich leider nicht wie man EOF matched...

Könnte ja bei Vorhandensein die Datei slurpen, den neuen Kram appenden, uniquifien und dann burpen, aber das hat mit der ursprünglichen Intention und S^I nix mehr zu tun :rock:

Und da ich einer bin der immer mit dem Kopf durch die Wand will nach dem Motto "Muß doch gehen" frage ich mich halt wie ich das Problem mittels $^I lösen könnte...

Danke noch Mal for @input!

DoN
drum&bass is a state of mind
renee
 2007-03-13 13:41
#74985 #74985
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Welche Probleme?

Wenn Du Perl > 5.7 verwendest, ist es ein Standard-Modul. Ansonsten kannst Du Dir ja mal anschauen wie das Problem in Tie::File gelöst ist.
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
dukeofnukem
 2007-03-13 15:02
#74986 #74986
User since
2007-01-15
47 Artikel
BenutzerIn
[default_avatar]
Das Problem des erst appenden, dann komplett einlesen, dann komplett überschreiben, alles mit nur einem fh.

Habe leider keinen Schimmer wie ich in <> navigieren kann :(

Schließlich muß ich erst an vorhandenen input appenden bevor ich nach dupes checken kann, dazu müßte ich ans Ende der Datei (und danach zum komplett einlesen wieder an den Anfang), weiß aber nicht wie man EOF matched.

Momentan slurpe ich in ein Array, appende meinen zusätzlichen Output ans Array, uniquifye selbiges (schönes Wort) und burpe. Funktioniert, ist wahrscheinlich auch nicht allzu foobar aber ich wollte es eben mal mit S^I probieren.

Bin leider relativer n00b was Perl angeht, insbesondere die systemnahen magischen Sachen :blush:

Donkschön nomma,

DoN
drum&bass is a state of mind
betterworld
 2007-03-13 15:58
#74987 #74987
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Warum willst Du denn nur einen fh verwenden?  Und warum scheiden externe Module aus?  (Und wie renee schon sagt: Tie::File ist nicht mehr extern..)

Wenn die Daten nicht so groß sind, dass sie ohne Probleme zwei Mal auf die Festplatte passen, wäre so etwas auch gut:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
# Einlesen
open my $in, '<', $filename or die $!;
my $daten = do {local $/; <$in>};
close $in;

# Verarbeiten
tu_was_mit($daten);

# Ausgeben
open my $out, '>', $filename . '.tmp' or die $!;
print $out $daten or die $!;
close $out or die $!;
rename($filename . '.tmp', $filename) or die $!;


Ansonsten gibt es eben auch noch die Variante:
Code: (dl )
1
2
3
4
5
6
7
open my $f, '+<', $filename or die $!;
my daten = do {local $/; <$f>};
tu_was_mit($daten);
seek($f, 0, 0) or die $!;
truncate($f, 0) or die $!;
print $f $daten or die $!;
close $f or die $!;


Aber ich kann es nicht oft genug betonen: Hände weg von der zweiten Variante:  Denn wenn dein Programm an der richtigen Stelle abstuerzt (oder der ganze Rechner), ist die Datei leer und die Daten futsch.

Dieses Problem hat die erste Variante nicht.  Ausserdem ist die Änderung an der Datei in der ersten Variante atomar, d. h. immer wenn jemand die Datei ausliest, ist sie vollstaendig und enthaelt nicht irgendwelches teilweise geschriebene Zeug.  (Wenn Dein Programm mehrfach gleichzeitig laufen koennte, brauchst Du aber wahrscheinlich trotzdem ein flock.)

Quote
Bin leider relativer n00b was Perl angeht

Dann solltest Du wohl doch nicht zu viel mit $^I rumspielen ;) Sorry, dass ich Dir davon erzaehlt habe.
dukeofnukem
 2007-03-13 18:28
#74988 #74988
User since
2007-01-15
47 Artikel
BenutzerIn
[default_avatar]
*g* Ok, dann laß ich $^I erstmal beiseite.

Ging mir eigentlich nur um Geschwindigkeit, aber denke mal daß slurpen und burpen nicht sooo langsam ist (und verhindert auch die crash-Problematik).

Wollte halt die mehrfache filehandle-Orgelei vermeiden, denn es handelt es sich um die Extraktion aus mehreren großen (4GB) logfiles (die natürlich nicht geslurped werden) während die Ergebnisse in sehr kurzen Dateien (~300-500 Byte) landen, von denen aber entsprechend ca. 15000 an der Zahl vorhanden sind. Zusätzlich läuft das Ganze auch noch via NFS auf ein dynamisches ClearCase View (MVFS). Da sind die Kosten für fhs dann schon eine Überlegung wert ;)

Leider handelt es sich um recht konservative Umgebungen auf die ich keinen administrativen Zugriff habe; Linux am einen Ende und Solaris am anderen Ende des NFS-Drahtes und das script soll auf beiden Maschinen (und beliebig auf anderen zukünftigen) mit der vorgegebenen Installation von Perl laufen (momentan 5.6.1 auf der Solariskiste)

Danke noch Mal, will keep on RTFM

DoN
drum&bass is a state of mind
renee
 2007-03-13 19:01
#74989 #74989
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Man kann Module auch in eigene Verzeichnisse installieren. So könntest Du das Modul in das Verzeichnis, in dem auch das Skript liegt, installieren und verwenden...
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
topeg
 2007-03-13 23:04
#74990 #74990
User since
2006-07-10
2611 Artikel
BenutzerIn

user image
Du kannst die datei doch stückchenweise durchsuchen und dier die relevanten Stellen mit Hilfe von "tell" merken. Dann springst du mit "seek" die relevanten stellen an und extrahierst was du wissen willst (read). Du brauchst nicht die gesammte Datei im speicher zu halten, oder übers Netztwerk zu ziehen. Das scheiben kannst du wohl kaum verringen, aoßer du nutzt "syswrite" um an eine exakte stelle in der der Zieldatei zu schreiben, das bringt aber nur etwas, wenn der neue Eintrag exakt die selbe Länge hat wie der alte. Sowas habe ich schon benutzt um Timestamps zu modifizieren, oder ähnliche Einträge mit fixer Länge.
<< |< 1 2 >| >> 14 Einträge, 2 Seiten



View all threads created 2007-03-12 19:06.