Help with script to get backup log status on windows systems

Discussion in 'Perl Misc' started by Matt Williamson, Jul 19, 2006.

  1. The purpose of this script is to quickly get a status on all of my nightly
    backups. It's working for everything but the .xml backup files. There are
    multiple .xml files created each night for the various processes that occur,
    the actual backup log is only one of them. I've determined that the .xml
    backup log files that I want are all encoded utf16le and the others are
    utf8. I only want to read the utf16le .xml files and print the server name
    and filename for just the latest of those but I can't figure out the best
    way to do it. Also, if you have any suggestions on the script in general,
    please feel free to comment. I'm new at this, so any advice from experienced
    coders is welcome and appreciated.

    use strict;
    use warnings;

    my @content;
    my @header;

    my %belogdirs = (
    "\\\\Server1" => "\\d\$\\Backup Exec\\data\\bex*.txt",
    \\\\Server2 => "\\c\$\\Program Files\\Seagate Software\\Backup
    Exec\\Nt\\Data\\bex*.txt",
    \\\\Server3 => "\\c\$\\Program Files\\Veritas\\Backup
    Exec\\Nt\\Data\\bex*.txt",
    \\\\Server4 => "\\c\$\\Program Files\\Veritas\\Backup
    Exec\\Nt\\Data\\bex*.txt",
    \\\\Server5 => "\\c\$\\Program Files\\Veritas\\Backup
    Exec\\Nt\\Data\\bex*.txt",
    \\\\Server6 => "\\c\$\\Program Files\\Veritas\\Backup
    Exec\\Nt\\Data\\bex*.xml"
    );

    open FILE, ">C:\\Backup Tape Log.txt";
    foreach my $server (keys %belogdirs) {
    my $fullpath = $server.$belogdirs{$server};
    my @files = `dir "$fullpath" /OD /B`;
    foreach my $file (reverse @files) {
    $fullpath =~ s/bex\*\.xml|bex\*\.txt/$file/i;
    chomp $fullpath;
    open F, $fullpath or die "can't open $file: $!\n";
    read F, my $buffer, 2;
    @header = unpack "h*", $buffer;
    close F;

    if ($header[0] =~ /ffef/i) { #handle the utf16 .xml files
    open F, "<:encoding(utf16le)", $fullpath or
    die "can't open $file: $!\n";
    @content = <F>;
    close F;
    }
    elsif ($header[0] =~ /efff/i) { # just in case?
    open F, "<:encoding(utf16be)", $fullpath or
    die "can't open $file: $!\n";
    @content = <F>;
    close F;
    }
    else { # handle the .txt files
    open F, $fullpath or
    die "can't open $file: $!\n";
    @content = <F>;
    close F;
    }

    # $server =~ s/\\\\/Server: /;
    # print FILE $server, "\n";
    # print FILE "Log File: ",$fullpath,"\n";

    foreach my $line (@content){
    if ($line =~ /(job (?:started|ended|completion status)):\s*(.*?)\s*$/i) {
    my ($job_type, $status) = ($1, $2);
    print FILE "$job_type: $status\n";
    }
    }
    print FILE "\n";
    last;
    }
    }

    close FILE;
    exec("notepad C:\\Backup Tape Log.txt");


    TIA

    Matt
     
    Matt Williamson, Jul 19, 2006
    #1
    1. Advertising

  2. So, getting the first 2 bytes of the file, converting them to hex and
    checking the string representation of the hex characters isn't the right way
    to do it? That part seems to work fine. What is a better way?
    It took me 4 hours to come up with that <g>

    Matt

    "A. Sinan Unur" <> wrote in message
    news:Xns98057A8FCC814asu1cornelledu@127.0.0.1...
    > "Matt Williamson" <> wrote in
    > news:e9lkp8$9gt$:
    >
    >> if ($header[0] =~ /ffef/i) { #handle the utf16 .xml files

    >
    > I can't comment on the rest of your post, but here you are looking for a
    > sequence of characters 'f', 'f', 'e', 'f' anywhere in the string. Those
    > characters have nothing to do with the BOM.
    >
    > http://www.unicode.org/faq/utf_bom.html#BOM
    >
    > Sinan
    >
    > --
    > A. Sinan Unur <>
    > (remove .invalid and reverse each component for email address)
    >
    > comp.lang.perl.misc guidelines on the WWW:
    > http://augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
    >
     
    Matt Williamson, Jul 19, 2006
    #2
    1. Advertising

  3. Matt Williamson

    Ben Morrow Guest

    Quoth "Matt Williamson" <>:
    > The purpose of this script is to quickly get a status on all of my nightly
    > backups. It's working for everything but the .xml backup files. There are
    > multiple .xml files created each night for the various processes that occur,
    > the actual backup log is only one of them. I've determined that the .xml
    > backup log files that I want are all encoded utf16le and the others are
    > utf8. I only want to read the utf16le .xml files and print the server name
    > and filename for just the latest of those but I can't figure out the best
    > way to do it. Also, if you have any suggestions on the script in general,
    > please feel free to comment. I'm new at this, so any advice from experienced
    > coders is welcome and appreciated.
    >
    > use strict;
    > use warnings;


    Good :).

    > my @content;


    This variable is not needed at this scope. You should declare it inside
    the first loop, as that's where you use it.

    > my @header;


    This variable is only needed per-file, so you should declare it
    per-file. But see below for why I don't think you need it at all... :)

    > my %belogdirs = (
    > "\\\\Server1" => "\\d\$\\Backup Exec\\data\\bex*.txt",
    > \\\\Server2 => "\\c\$\\Program Files\\Seagate Software\\Backup
    > Exec\\Nt\\Data\\bex*.txt",
    > \\\\Server3 => "\\c\$\\Program Files\\Veritas\\Backup
    > Exec\\Nt\\Data\\bex*.txt",
    > \\\\Server4 => "\\c\$\\Program Files\\Veritas\\Backup
    > Exec\\Nt\\Data\\bex*.txt",
    > \\\\Server5 => "\\c\$\\Program Files\\Veritas\\Backup
    > Exec\\Nt\\Data\\bex*.txt",
    > \\\\Server6 => "\\c\$\\Program Files\\Veritas\\Backup
    > Exec\\Nt\\Data\\bex*.xml"
    > );


    This would be easier with forward slashes and single quotes:

    '//Server1' => '/d$/Backup Exec/data/bex*.txt',

    > open FILE, ">C:\\Backup Tape Log.txt";


    It is generally safer to use lexical FHs and three-arg open, and you
    should *always* check the return value:

    open my $FILE, '>', 'c:/Backup Tape Log.txt'
    or die "can't open 'c:/Backup Tape Log.txt': $!";

    > foreach my $server (keys %belogdirs) {
    > my $fullpath = $server.$belogdirs{$server};
    > my @files = `dir "$fullpath" /OD /B`;


    OK, now you'll hit the only snag of using forward slashes: cmd.exe and
    most native NT commands don't like it :). You can either convert, with
    either a simple s!/!\\!g or with File::Spec::Functions::canonpath, or
    (probably better) you can use (I guess) glob to do the globbing in Perl.
    (I'm not entirely sure glob will do what you want, as I don't know what
    /OD /B means to dir, but you can certainly emulate it with either glob
    or File::Find.)

    > foreach my $file (reverse @files) {
    > $fullpath =~ s/bex\*\.xml|bex\*\.txt/$file/i;
    > chomp $fullpath;


    Ick. This will work, I guess, but it's pretty nasty. I think at this
    point I'd do the whole thing a little differently, starting with a
    datastructure more like

    my %belogdirs = (
    Server1 => {
    path => 'd$/Backup Exec/data',
    extn => 'txt',
    },
    ...
    Server6 => {
    dir => 'c$/Program Files/Veritas/Backup Exec/Nt/Data',
    extn => 'xml',
    },
    );

    for (keys %belogdirs) {
    my $glob = "//$_/$belogdirs{$_}{path}/bex*.$belogdirs{$_}{extn}";

    Actually, although I normally hate it (in Perl), that would probably be
    clearer as a sprintf:

    my $glob = sprintf '//%s/%s/bex*.%s' =>
    $_, $belogdirs{$_}{path}, $belogdirs{$_}{extn};

    for (reverse glob $glob) {
    open my $F, '<', $_ or die ...;

    Note that glob returns full paths, rather than just a list of names.

    > open F, $fullpath or die "can't open $file: $!\n";
    > read F, my $buffer, 2;
    > @header = unpack "h*", $buffer;


    Why are you using an array when you only have one return value?

    > close F;
    >
    > if ($header[0] =~ /ffef/i) { #handle the utf16 .xml files


    You're not doing a pattern match here, you are checking for equality.
    But you don't need to: if you can be sure your XML files will always
    have a BOM, you can simply open the file :encoding(utf16). This will
    throw an exception on the first read if there isn't a BOM. So,
    continuing from the above:

    for my $file (reverse glob $glob) {
    my $mode;
    $belogdirs{$_}{extn} eq 'xml'
    and $mode = ':encoding(utf16)';

    # I don't really like file extensions, but if you've got
    # 'em you might as well use 'em...

    open my $F, "<$enc", $file
    or die "can't open '$file': $!";

    eval {
    @content = <$F>;
    1;
    } or next;
    }

    This will skip onto the next file if the read throws an exception.

    > open F, "<:encoding(utf16le)", $fullpath or
    > die "can't open $file: $!\n";
    > @content = <F>;
    > close F;
    > }
    > elsif ($header[0] =~ /efff/i) { # just in case?
    > open F, "<:encoding(utf16be)", $fullpath or
    > die "can't open $file: $!\n";
    > @content = <F>;
    > close F;
    > }
    > else { # handle the .txt files
    > open F, $fullpath or
    > die "can't open $file: $!\n";
    > @content = <F>;
    > close F;
    > }
    >
    > # $server =~ s/\\\\/Server: /;
    > # print FILE $server, "\n";
    > # print FILE "Log File: ",$fullpath,"\n";
    >
    > foreach my $line (@content){
    > if ($line =~ /(job (?:started|ended|completion status)):\s*(.*?)\s*$/i) {
    > my ($job_type, $status) = ($1, $2);
    > print FILE "$job_type: $status\n";


    If you set $\ = "\n" then Perl'll print them for you.

    > }
    > }


    It really helps a lot to keep you indentation sane.

    > print FILE "\n";
    > last;


    I don't understand what this is here for?

    > }
    > }
    >
    > close FILE;
    > exec("notepad C:\\Backup Tape Log.txt");


    Does that work? It didn't ought to: the filename has spaces in it. I
    would recommend using exec LIST so Perl does the nasty cmd.exe quoting
    for you, even though it's not strictly as safe is it is on a real
    platform:

    exec notepad => 'c:\\Backup Tape Log.txt';

    I would also put this filename in a variable. Never write the same thing
    twice.

    Ben

    --
    Outside of a dog, a book is a man's best friend.
    Inside of a dog, it's too dark to read.
    Groucho Marx
     
    Ben Morrow, Jul 19, 2006
    #3
  4. Matt Williamson

    -berlin.de Guest

    A. Sinan Unur <> wrote in comp.lang.perl.misc:
    > "Matt Williamson" <> wrote in
    > news:e9lohn$jip$:
    >
    > > "A. Sinan Unur" <> wrote in message
    > > news:Xns98057A8FCC814asu1cornelledu@127.0.0.1...
    > >> "Matt Williamson" <> wrote in
    > >> news:e9lkp8$9gt$:
    > >>
    > >>> if ($header[0] =~ /ffef/i) { #handle the utf16 .xml files
    > >>
    > >> I can't comment on the rest of your post, but here you are looking
    > >> for a sequence of characters 'f', 'f', 'e', 'f' anywhere in the
    > >> string. Those characters have nothing to do with the BOM.
    > >>
    > >> http://www.unicode.org/faq/utf_bom.html#BOM

    >
    > > So, getting the first 2 bytes of the file, converting them to hex and
    > > checking the string representation of the hex characters isn't the
    > > right way to do it? That part seems to work fine. What is a better
    > > way? It took me 4 hours to come up with that <g>

    >
    > I did not realize that was what you were doing.


    It is a rather roundabout way of checking the first two bytes of
    a string.

    read F, my $buffer, 2;
    @header = unpack "h*", $buffer;
    if ($header[0] =~ /ffef/i) { #handle the utf16 .xml files

    Instead, define a string constant that contains the relevant two
    bytes and compare (untested):

    use constant BOM => "\xFF\xEF";

    read F, my $buffer, 2;
    if ( $buffer eq BOM ) { # handle the utf16 .xml files

    Anno
     
    -berlin.de, Jul 20, 2006
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.

Share This Page