Gaurav99 has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I have a log file that needs to be parsed. The logic is to find the word " Error at " (case insensitive) in the text file and once found read backwards to to find the first occurrence of string starting with word executing: (something like below) executing: @C:\parse_test\test\dir1\dir1.sql and once found delete or unlink the file at the path. In this case delete dir1.sql and not dir2.sql. The log file has the below contents
connecting to Oracle with C:\Oracle\instantclient/sqlplus. executing: @C:\parse_test\test\dir1\dir1.sql alter tablexxxxxx add xxxxxxxxL VARCHAR2(3) * ERROR at line 1: ORA-01430: column being added already exists in table ERROR at line 37: ORA-01430: column being added already exists in table Comment created. Finished execution. connecting to Oracle with C:\Oracle\instantclient/sqlplus. executing: @C:\parse_test\test\dir2\dir2.sql alter table XXXXXXXXXX rename column XXXXXX to PXXXXXXX Table altered. Comment created. Finished execution.
so far I have
#!/usr/bin/perl {local $/;$_=<>} unlink $1 or warn "$1 $!" while /executing: \@(.*)(?ms:.*?ERROR at)/g;
Running this as test.pl C:\logfile\dir\log.txt It seems to be inconsistently working. Sometimes it deletes files that got successfully executed.

Replies are listed 'Best First'.
Re: Parse the text file output and delete files
by Laurent_R (Canon) on Jul 20, 2015 at 16:52 UTC
    Just parse your file once and only forward.

    Keep track in a variable at all time of the last executing: @ ... dir1.sql encountered, and, when you meet an ERROR line, you know that you can delete the SQL file whose name is in the last-sql-file variable (if the file still exists).

    No need to read backward.

      I am very new to perl.Will really appreciate if I have the script for the logic above.

        I would explore the tutorials you can find here. Or google "perl basic file operations". Make some effort - you'll learn more that way!

        Dum Spiro Spero
        Hi,

        the general basic idea might perhaps be something like this:

        my $last_sql_file; while (my $line = <$LOG>) { chomp $line; $last_sql_file = $1 if $line =~ /^executing: \@(.+)/; if ($line =~ /^ERROR at/) { unlink $last_sql_file if -f $last_sql_file; # don't try to del +ete this file if it was already deleted on a previous error } }
        That's the basic algorithm, please fell free to ask if you don't understand it. I leave it to you to find out in the documentation how to open the log file if you don't know how (clue: take a look at open).
Re: Parse the text file output and delete files
by Anonymous Monk on Jul 20, 2015 at 18:08 UTC

    Untested :). Also assumes case sensitive, in contradiction to your spec AND your code.

    #!/usr/bin/perl use strict; $| = 1; $/ = 'ERROR at '; while(<>) { /ERROR at / && /[\0-\xff]*^executing: \@(.+)/m and (unlink $1 or warn "$1 $!"); }

      Slight (hehehe) tweak for case insensitive.

      #!/usr/bin/perl use warnings; use strict; $| = 1; do{ local $/; $_ = <> }; $1 =~ /[\0-\xff]*^executing: \@(.+)/m and (unlink $1 or warn "$1 $!") while /(.*?)ERROR at/gi;
      Tried executing the above script got the below error C:\parse_test\test\dir1\dir1.sql at C:\test\script\per_test.pl line 9, <> chunk 2. C:\parse_test\test\dir1\dir2.sql at C:\test\script\per_test.pl line 9, <> chunk 4.

        Please put the error message in <code> tags.

        It doesn't look like there is an error, just an already unlinked file, but it's unreadable without code tags.

        Also, it looks like you typo'd the $!

      So, do you really need case insensitive ?

Re: Parse the text file output and delete files
by locked_user sundialsvc4 (Abbot) on Jul 20, 2015 at 18:55 UTC

    You might be interested to read up on the awk utility, which is one of the inspirations for Perl and still a very good tool for dealing with requirements like these.   An awk program basically consists of a set of regular-expressions and blocks of code that are to be executed when the associated pattern is matched.   awk reads the source-file line by line, testing each line.   Not surprisingly, this translates easily and directly to Perl programs.

    First the explanation, then the sample code.   In this problem, there are basically two “lines of interest.”   There is the executing: line, which contributes the name of the last script executed, and the ERROR at line, which tells us that we have something to do (with that name).

    The meat-and-potatoes of the program will therefore look something like this:   (extemporaneous code, untested)

    #!/usr/bin/perl use strict; use warnings; $| = 1; my $filename; while (<>) { if ( /^executing: \@(.*)/i ) { $filename = $1; } else if ( /^ERROR at/i ) { unlink $filename or warn "can't unlink $filename: $!\n"); } }

    Note the following:

    1. For brevity, and maybe also to allow the easy writing of “awk-script replacement” programs, Perl uses a number of cryptic variables such as $_ and $!.   For more information, see perldoc perlvar.   The if statements implicitly refer to “the last line read,” which is $_ and populated by the while loop ... actually by the equally-cryptic “<>” therein.
    2. The regular expressions shown here use a number of features.   (See perldoc perlre et al):
      • The /i suffix makes them case-insensitive.
      • The use of ^ to “anchor” the pattern to the beginning of the string.
      • The use of a parenthesized group to capture what matches inside the group and to make it available, in this case, as $1.

    HTH ...