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

Hi Monks, I will be grateful if you can provide me a code for this problem. I am new to perl and you help will be really appreciated. I have a log file which looks like this:- (I have numbered the lines)
1 Software Package: "xxxxxxx"
2 Operation: install
3 Mode: not-transactional,not-undoable | force
4 Time: XXXXXXX
5 =================
6 WORKSTATION1:
7
8 Distribution ID: `xxxx.xxx'
9
10 Current software package status is 'IC--E'.
11
12 Operation successful.
13
14 Execution of user program 'during_install - C:\Staging\MS07-061_WXP_ENU.1.0.1.0\setup.exe (/s)' completed with result: 'success'.
15
16 User program exit code: 0
17
18
19 =================
20
21 Software Package: "XXXXX"
22 Operation: install
23 Mode: not-transactional,not-undoable |force
24 Time: XXXXXX
25 =================
26 WORKSTATION2:
27
28 Distribution ID: `XXXXX.XXXX'
29
30 Current software package status is 'IC--E'.
31
32 Operation unsuccessful.
33
34 Execution of user program 'during_install - C:\Staging\MS07-061_WXP_ENU.1.0.1.0\setup.exe (/s)' completed with result: 'success'.
35
36 User program exit code: 0
37
38
39 =================
40 and so on follows the same pattern.

### Program will find for the match 'IC--E'

The output file will look like this:
WORKSTATION1(Line 6) User program exit code: 0(Line 16)
WORKSTATION2(Line 26) User program exit code: 0(Line 36)

Replies are listed 'Best First'.
Re: Parsing a Log file
by CountZero (Bishop) on Dec 16, 2007 at 20:36 UTC
    Am I right in assuming that your log file comes in blocks of 20 lines?

    If so, just read in your log file in blocks of 20 lines, put each line in an array and check $array[9]=~m/IC--E/. If this is true, print "@array[5, 15]\n";Perhaps it can look like this:

    use strict; while (1) { my @array; foreach (1..20) { my $line = <DATA>; chomp $line; push @array, $line; } print "@array[5, 15]\n" if $array[9]=~m/IC--E/; last if eof; } __DATA__ 1 Software Package: "xxxxxxx" 2 Operation: install 3 Mode: not-transactional,not-undoable | force 4 Time: XXXXXXX 5 ================= 6 WORKSTATION1: 7 8 Distribution ID: `xxxx.xxx' 9 10 Current software package status is 'IC--E'. 11 12 Operation successful. 13 14 Execution of user program 'during_install - C:\Staging\MS07-061_WXP +_ENU.1.0.1.0\setup.exe (/s)' completed with result: 'success'. 15 16 User program exit code: 0 17 18 19 ================= 20 21 Software Package: "XXXXX" 22 Operation: install 23 Mode: not-transactional,not-undoable |force 24 Time: XXXXXX 25 ================= 26 WORKSTATION2: 27 28 Distribution ID: `XXXXX.XXXX' 29 30 Current software package status is 'IC--E'. 31 32 Operation unsuccessful. 33 34 Execution of user program 'during_install - C:\Staging\MS07-061_WXP +_ENU.1.0.1.0\setup.exe (/s)' completed with result: 'success'. 35 36 User program exit code: 0 37 38 39 =================

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Parsing a Log file
by FunkyMonk (Bishop) on Dec 16, 2007 at 22:46 UTC
    You can use an input record separator of =================\n to read the log in more meaningful chunks. split can split the chunks into lines. Then you'll be interested in lines 0 (workstation), 4 (status) and 10 (exit code) of each chunk. Check for the specified status using m//, the match operator, and print if there's a match using an array slice.

    $/ = "=================\n"; while ( <DATA> ) { my @lines = split /\n/; next unless $lines[4] =~ /'IC--E'/; print "@lines[0, 10]\n"; }

    update: Slightly clearer(?) logic:

    $/ = "=================\n"; while ( <DATA> ) { my @lines = split /\n/; print "@lines[0, 10]\n" if $lines[4] =~ /'IC--E'/; }

Re: Parsing a Log file
by pc88mxer (Vicar) on Dec 16, 2007 at 20:33 UTC
    Two things:

    1. Please preview your question before you post it. The contents of your log file would be a lot easier to read if it was in a <pre> ... </pre> block. Update: Thanks for reformatting it.

    2. What information do you want to extract from the log file? We need to know this in order to help you. Update: Okay, now I see what you want to parse.

    Here's one way of doing it:

    my $r; while (<>) { if (/^Current software package status is: *(.*)/) { $r->{package_status} = $1; } elsif (/^(WORKSTATION\d+):/) { $r->{workstation} = $1; $r->{workstation_line} = $. } elsif (/^(User program exit code: .*)/) { $r->{exit_message} = $1; $r->{exit_line} = $. # process $r if ($r->{package_status} eq "IC--E") { print "$r->{workstation}(line $r->{workstation_line}) $r->{exit_ +message}(line $r->{exit_line}) } $r = {}; # clear variables for next record } }
    This assume that package status line precedes workstation line which precedes the exit message line.
      The contents of your log file would be a lot easier to read if it was in a <pre> ... </pre> block.
      While nobody can deny this, it's usually better to suggest <code>...</code> (or shorter <c>...</c>) blocks for formatting code, file contents, printouts from shell sessions, etc. In the first two cases, in particular, this eases the other monks with stuff like downloading the fragment.

      Hey! Up to Dec 16, 2007 I was named frodo72 here, take note of the change! Flavio
      perl -ple'$_=reverse' <<<ti.xittelop@oivalf

      Io ho capito... ma tu che hai detto?
        On the contrary, I can deny it. To quote the bottom of the preview page:

        If you think you're going to use <pre> tags — don't! Use <code> tags instead! This applies to data as well as code.

        Aside from downloadability, using <code> instead of <pre> also prevents long lines from messing up page formatting.

Re: Parsing a Log file
by lihao (Monk) on Dec 16, 2007 at 23:13 UTC
    If your log file are organized in that order, (I mean "WORKSTATION" and "exit code" come one by one and in a fixed order), then you can just use:
    #!/usr/bin/perl use warnings; use strict; open my $fh, '<', 'log.txt' or die "cant open log.txt:$!"; my $workstations; while (<$fh>) { next if /^\s*$/; if (/(WORKSTATION\d+)/) { $workstations = "$1(Line $.)"; } elsif (/(User program exit code:\s*\d+)/) { print "$workstations $1(Line $.)\n"; } }

    If "WORKSTATION.." is at the beginning of a line as it seems to be in your example, change the regex to ^(WORKSTATION\d+) which makes it better/faster to suit your problem. so does 'User program exit code:' line...

    Regards

    lihao(XC)