egrep in shell scripting

Viewed 162

Can anyone please help here. If not feasible with egrep, other suggestions too would be highly appreciated.

I am trying to write an egrep expression to grep ORA-00942 with "UTL_RECOMP_SLAVE_75", when it is in the next line of "UTL_RECOMP_SLAVE_75" string. However ORA-00942 in any other line in the file should be ignored. ORA-00942 has to be grepped only when its in the next line followed by "UTL_RECOMP_SLAVE_75".

ORA-12012: error on auto execute of job "SYS"."UTL_RECOMP_SLAVE_75"
ORA-00942:     <<<<<<<<<<<<<<<<<< should be grepped along with "UTL_RECOMP_SLAVE_75"

ORA-00490: error text is xyz
ORA-00942:   <<<<<<<<<<<<<<<< this should be ignored
5 Answers

Here is a simple solution with grep.

grep -A1 UTL_RECOMP_SLAVE_75 inputfile | grep -B1 ORA-00942

You can do it with two piece of grep. Because grep has a handy option -An and -Bn.

e.g.: grep UTL_RECOMP_SLAVE_75 -A3 inputfile

Print 3 lines of trailing context after matching lines and -Bn option is: Print n lines of leading context before matching lines.
pls read in man grep.

In this answer I piped the first grep to the 2nd. But you can do it in two steps also with a tmp_file instead of pipe to see how works grep commands.

grep -A1 UTL_RECOMP_SLAVE_75 inputfile >tmp_file
grep -B1 ORA-00942 tmp_file

And you can give the matching more precisely if you need it. e.g.:

grep -A1 -w UTL_RECOMP_SLAVE_75 inputfile | grep -B1 -w ^ORA-00942

Using sed:

sed -n '/"UTL_RECOMP_SLAVE_75"/{N;/\n.*ORA-00942/p;D}'

Here is a Perl solution. It's not as short as the sed answer, but I find it more readable:

perl -nle 'print if (/^ORA-00942/ && $wanted); if (/UTL_RECOMP_SLAVE_75/) {$wanted=1} else {$wanted=0}' /your/file

Or a slightly shorter version:

perl -nle 'print if (/^ORA-00942/ && $wanted); $wanted=(/UTL_RECOMP_SLAVE_75/ ? 1 : 0)' /your/file

This sets a $wanted flag to true if the current line contains "UTL_RECOMP_SLAVE_75", or to false if it doesn't.

This prints only lines that start with "ORA-00942", and only when $wanted is true.

If you also want to print the previous line, use Ed Morton's solution instead, which prints both.

This will do what you want using any awk in any shell on every UNIX box:

$ awk '/ORA-00942/ && (p ~ /UTL_RECOMP_SLAVE_75/){print p ORS $0} {p=$0}' file
ORA-12012: error on auto execute of job "SYS"."UTL_RECOMP_SLAVE_75"
ORA-00942:     <<<<<<<<<<<<<<<<<< should be grepped along with "UTL_RECOMP_SLAVE_75"

I would advise a combination of grep and awk:

You can use grep -n in order to get the lines, containing "ORA-00942", you get something like:

2:line_2 contains ORA-00942
8:line_5 contains ORA-00942

From that, using awk with ':' as a field separator, you can get the line numbers which contain "ORA-00942" ([2,8]).

You can then use grep -n in order the lines, containing "UTL_RECOMP_SLAVE_75", which might be something like:

4:line_4 contains UTL_RECOMP_SLAVE_75
7:line_7 contains UTL_RECOMP_SLAVE_75

Using awk again you get the line numbers which contain "UTL_RECOMP_SLAVE_75" ([4,7]).

From there you process both lists of line numbers in order to get the matching ones.

Tips and tricks: instead of keeping the lines, containing "UTL_RECOMP_SLAVE_75", you take the line numbers plus one (instead of [4,7] you have [5,8]), it might prove easier to find two lists with the same numbers than to check on the "plus one" values.

Related