How to get lines in between Patterns?

Hi,

I need to create a script that does the following:

  1. Read the file for the occurrences of "EXECUTE" and "END" strings.
    There will be several occurrences of EXECUTE and END strings on the file.
  2. The resulting lines in #1, needs to be searched for the word "Error|failed|error|warning", if the word error occurs on the line, prints the specific line and 1 line above it.

Here's a sample input file:

-------------------------------< EXECUTE >----------------------------
This session consists of:  Feature  R-state        Install-type Upgrade-from

------------------------------------------------------------------------  
conf_system                              R3C01          upgrade      R3B03     
------------------------------------------------------------------------
------------------< CIF(conf) 2008-04-08 02:31:28 >--------------------
----------------------< END 2008-04-08 02:31:57 >---------------------

-------------------------------< EXECUTE >-----------------------------
---------< FM(fm_core,fmba,fmaa,fmx,fmav) 2008-04-08 03:14:09 >-------
[main] Error installing feature [fmx] - com.er.nm.if.ist.NoMatchingUpgradeClauseFoundException: 
--------------------------< END 2008-04-08 03:22:51 >------------------

The output should be:

---------< FM(fm_core,fmba,fmaa,fmx,fmav) 2008-04-08 03:14:09 >-------
[main] Error installing feature [fmx] - com.er.nm.if.ist.NoMatchingUpgradeClauseFoundException: 

I'm not familiar with awk command, and if you can guide me on this please?
Any help is greatly appreciated.

//racbern

try this

#! /usr/bin/ksh

cat linebetween.d | awk '
/EXECUTE/ { start = 1; nextline=1; continue }
/END/ { start = 0 ; continue}
(/Error/ || /failed/ || /error/ || /warning/) && start == 1 { print hesderline; print $0 }
{ if (nextline == 1 ) {hesderline = $0; nextline=0}}
'

Hi aju_kup,

Thanks for your quick reply.

I tried your suggestion, and it only prints the lines with the patterns "Error|failed|error|warning". I need to get also the line above it.

I did some trial and error of the awk command and come out with the following (but I dont know how to get the line above it):

# awk '/EXECUTE/,/END/ { if ($0 ~ /Error installing/) print $0}' /var/tmp/llsva.log

//racbern

#! /usr/bin/ksh

cat linebetween.d | awk '
/EXECUTE/ { start = 1; continue }
/END/ { start = 0 ; continue}
(/Error/ || /failed/ || /error/ || /warning/) && start == 1 { print pline; print $0 }
{ pline = $0}
'

Hi aju kup,

Thanks for your quick reply.

Can you explain me the code as well please?

//racbern

The cat is useless, of course, and you can combine all those search expressions into one big ole regular expression:

awk '/EXECUTE/ { start = 1; continue }
/END/ { start = 0; continue }
start && /[Ee]rror|failed|warning/ { print pline; print }
{ pline = $0 }' file

The essential point is that pline captures the previous line when it wasn't printed; and start is true when you have seen an EXECUTE but not yet an END. So when start is true and the regular expression matches, you print the previous line and the current line.

Hi era,

Thanks for reply as well.
I added == 1 on your suggestion, and it worked fine as well.

awk '/EXECUTE/ { start = 1; continue }
/END/ { start = 0; continue }
start == 1 && /[Ee]rror|failed|warning/ { print pline; print }
{ pline = $0 }' file

Is there a way I can assign the pline to a variable, so that I can manipulate it afterwards?

Thanks again..

//racbern

pline itself is variable

pline is a variable.

Edit: heh, posting clash battle of the giants (-:

Hi,

If I tried to print out $pline outside the awk command, its empty.

//racbern

Hi again,

The script doesnt work if the pline is a blank line.

How can I add a validation inside the awk command to check if pline is blank line to get the line above the blank line instead?

Thanks a lot again,

//racbern

awk '/EXECUTE/ { start = 1; continue }
/END/ { start = 0; continue }
start && /[Ee]rror|failed|warning/ { print pline; print }
/./ { pline = $0 }' file

The dot matches lines which contain a character. If you want a non-blank character, change the dot to something like [^ ]

The awk script handles the whole file in one go; by the time your shell regains control over execution, "pline" will no longer be meaningful.

Generally, to get both output on standard output and a value into a variable from a single child process is extremely tricky. My suggestion would be to make the awk script print its output in such a form that you can then unambiguously identify which lines are "plines" when reading the output back into the shell script. Something like

awk '... print "pline " pline; print "output " $0 ' ... |
while read type line; do
  case $type in
    pline) echo this is a pline: "$line";;
    output) echo this is real output for that pline: "$line";;
  esac
done

Do you see what I'm getting at? Make awk print a single extra identifier at the start of every line, so your shell script can read that and discard it, but then handle the different types of lines differently depending on what the value of the discarded identifier was.