[Perl] Windows-Dateien unter Unix bearbeiten

anddie

Well-known member
ID: 171
L
3 Mai 2006
2.270
133
Hi,

ich habe bei einem Script folgendes Problem.
Ich bekomme eine Textdatei von einem Windowssystem auf eine Solaris-Büchse.
Wenn ich die Datei mit dem vi anschaue, steht am Ende jeder Zeile ein ^M.
Das führt nun dazu, dass Perl bei einem chomp() dieses ^M nicht entfernt und somit einiges am weiteren Scriptverlauf schiefgeht.

Das einzige, was ich dazu gefunden habe, ist von das hier und dort steht: Genaugenommen entfernt chomp() am Ende eines Strings die Zeichenkette, die in der Variablen "$/" steht (Standardwert: "\n"). Ist "$/" leer, so werden alle Zeilenvorschübe abgeschnitten
Mir stellt sich jetzt die Frage, was genau ist $/. Eine Systemvariable, eine Variable von Perl oder was anderes. Eine Ausgabe von $/ liefert nix, also scheint es leer zu sein.

Alternativ meinte noch wer, dass es mit Perl eine Funktion gibt, um solche Windowszeichen korrekt zu verarbeiten. Aber dazu finde ich leider nichts.

anddie
 
"perdoc perlvar" - Auszug
Code:
$/      The input record separator, newline by default. This influences
        Perl's idea of what a "line" is. Works like awk's RS variable,
        including treating empty lines as a terminator if set to the
        null string. (An empty line cannot contain any spaces or tabs.)
        You may set it to a multi-character string to match a
        multi-character terminator, or to "undef" to read through the
        end of file. Setting it to "\n\n" means something slightly
        different than setting to "", if the file contains consecutive
        empty lines. Setting to "" will treat two or more consecutive
        empty lines as a single empty line. Setting to "\n\n" will
        blindly assume that the next input character belongs to the nex
        paragraph, even if it's a newline. (Mnemonic: / delimits line
        boundaries when quoting poetry.)

            local $/;           # enable "slurp" mode
            local $_ = <FH>;    # whole file now here
            s/\n[ \t]+/ /g;

        Remember: the value of $/ is a string, not a regex. awk has to
        be better for something. :-)

        Setting $/ to a reference to an integer, scalar containing an
        integer, or scalar that's convertible to an integer will attemp
        to read records instead of lines, with the maximum record size
        being the referenced integer. So this:

            local $/ = \32768; # or \"32768", or \$var_containing_32768
            open my $fh, $myfile or die $!;
            local $_ = <$fh>;

        will read a record of no more than 32768 bytes from FILE. If
        you're not reading from a record-oriented file (or your OS
        doesn't have record-oriented files), then you'll likely get a
        full chunk of data with every read. If a record is larger than
        the record size you've set, you'll get the record back in
        pieces.

        On VMS, record reads are done with the equivalent of "sysread",
        so it's best not to mix record and non-record reads on the same
        file. (This is unlikely to be a problem, because any file you'd
        want to read in record mode is probably unusable in line mode.)
        Non-VMS systems do normal I/O, so it's safe to mix record and
        non-record reads of a file.

        See also "Newlines" in perlport. Also see $..

Und was dein "Problem" angeht ... du musst "nur" die Zeilenenden von Wintendo auf *nix Enden ummodeln. Geht mit nem "Einzeiler"

Code:
perl -pi -e 's/\r\n/\n/g' file.txt

Damit sollte dir geholfen sein ;)
 
buggle schrieb:
Code:
perl -pi -e 's/\r\n/\n/g' file.txt

Damit sollte dir geholfen sein ;)
Ja oder einfacher die CRs entfernen:
Code:
perl -pi -e 's/\r//g' file.txt

Man kann das auch direkt ins Script einbauen. Ein gutes FTP-Programm wie WS-FTP macht solche Umwandlungen auch automatisch.
 
anddie schrieb:
Mir stellt sich jetzt die Frage, was genau ist $/. Eine Systemvariable, eine Variable von Perl oder was anderes. Eine Ausgabe von $/ liefert nix, also scheint es leer zu sein.
Bei mir steht da ein Newline-Zeichen drin.

Neben den genannten Lösungen gibt es Filterprogramme, such mal nach dos2unix. Sowas könnte bei großen Dateien schneller gehen, weil diese Filter meistens in C geschrieben sind.
 
Muss nicht ... die Perl Routinen sind ja auch fuer die Textbearbeitung gedacht und legen schon einiges an Tempo vor ... Der Aufruf von nem externen Program ist evl. nicht sonderlich empfehlenswert, da das ja das Vorhandensein, des Tools vorraussetzt ;)
 
Ich werde mal abklären, ob ich die Datei in Zukunft nicht in nem Unixformat bekommen kann. Bin nämlich blöderweise net der Owner von dem Teil und von daher würde ich sonst jede Behandlung eh zeilenweise machen.

Da die Daten pro Zeile mit nem Semikolon getrennt sind und bei den letzten Werten mich nur interessiert, ob dort ein Y oder N steht, hab ich vorerst aus meinem eq 'Y' einfach ein =~ /Y/ gemacht.

anddie
 
anddie schrieb:
Da die Daten pro Zeile mit nem Semikolon getrennt sind und bei den letzten Werten mich nur interessiert, ob dort ein Y oder N steht, hab ich vorerst aus meinem eq 'Y' einfach ein =~ /Y/ gemacht.

Ich bezweifle ob das die richtige Lösung ist. Was machst du, wenn eine "Y" sonstwo in der Zeile steht? Dann hast du nicht mehr den letzten Wert überprüft sondern was völlig anderes!

Die richtige Lösung wäre, mit dem Zeilenumbruch korrekt umzugehen. Ein sehr wichtiger Artikel, den jeder kennen sollte, der mit Daten aus verschiedenen Systemen arbeitet: https://perldoc.perl.org/perlport.html :)rtfm: Erster Abschnitt: Newlines)
Hier wird jedes Problem erklärt, daß dir mit \r und \n auftauchen kann und wieso es vielleicht nicht funktioniert, wie du dir das erhofft hast. Denn \n muss nicht gleich \n sein ;)

Du solltest auch die Codeschnippsel finden, die du mit kleinen Anpassungen für dein Problem nutzen kannst.