0

I have the following sed command that fetches back a whole chunk of a log that has an <interface> XML within it that's being printed into a txt file:

sed -n '/(StartingExpression)/{:start /<\/Interface>/!{N;b start};/(SomeValueInTheXML)"/p}' *.log > File.txt

The issue is that I have some dynamic values (time stamp) that's always 4 lines above the (StartingExpression)... is there a way to print something like:

line of (StartingExpression) -4

?

I found this question: search (e.g. awk, grep, sed) for string, then look for X lines above and another string below but the solution isn't really clear :\

Thanks for the help.

also, if anyone has a good source to learn sed, I'll be thankful if you post it :)

EDIT per request:

one
two
1/1/2015
line 1
12345
(StartingExpression)
<Interface>
   <A>
   Name=Andy
   </A>
</Interface>
three
four
1/1/2015
line 1
12345
(StartingExpression)
<Interface>
   <A>
   Name=John
   </A>
</Interface>
hello
world

I would like to print from 1/1/2015 (which is 3 lines above (StartingExpression) - this can be dynamic as its a date ) until </Interface>

EDIT: I forgot to mention, there can be multiple instances of these interfaces... How do you also ensure to print ONLY the one that has Name=Andy?

file.txt:

1/1/2015
line 1
12345
(StartingExpression)
<Interface>
   <A>
   Name=Andy
   </A>
</Interface>
Community
  • 1
  • 1
Gal Appelbaum
  • 1,905
  • 6
  • 21
  • 33
  • keep the last 4 previous line in holding buffer (with `h`, `H` or `x`) and add it when before printing (`g`, `G`or `x`) result (need reformat like standard line). Awk is certainly more adequat in this case). It help if there is some data sample, not only the code – NeronLeVelu Oct 27 '15 at 14:30

2 Answers2

1

This awk idiom will print n lines preceding and including pattern.

$awk -v n="$plines" '{a[p]=$0; p=(p+1)%n} /pattern/{for(i=p;i<p+n;i++) print a[i%n]}' file

For example

$awk -v n=5 '{a[p]=$0; p=(p+1)%n} /20/{for(i=p;i<p+n;i++) print a[i%n]}' <(seq 10 99)

16
17
18
19
20

UPDATE for additional logic.

You can incorporate printing after pattern match easily as well. This script will print the lines before; and now after the match until the end of close tag.

$ awk -v n=4 '           {a[p]=$0; p=(p+1)%n} 
   /(StartingExpression)/{for(i=p;i<p+n-1;i++) print a[i%n];f=1} 
                        f{print} 
            /\/Interface/{exit}' file

1/1/2015
line 1
12345
(StartingExpression)
<Interface>
   <A>
   </A>
</Interface>

UPDATE: filter based on attribute value

It's easier at this point just pass through another script instead of rewriting it. Replace the exit with f=0, the first script will output all the matching records and filter in a second script the record of interest.

$ awk -v n=4 '           {a[p]=$0; p=(p+1)%n}
   /(StartingExpression)/{for(i=p;i<p+n-1;i++) print a[i%n];f=1}
                        f{print}
            /\/Interface/{f=0}' file
| awk 'BEGIN{ORS=RS="</Interface>\n"} 
 /Name=Andy/'


1/1/2015
line 1
12345
(StartingExpression)
<Interface>
   <A>
   Name=Andy
   </A>
</Interface>
karakfa
  • 66,216
  • 7
  • 41
  • 56
  • I get what you did... What about the `(SomeValueInTheXML)` part? I am trying to ensure that I print only the XML (out of many duplicate XMLs with different values but same pattern) that has a specific value in it... what you have here is a value as pattern rather than an object (or in my case an XML)... how do I go about ensuring that anything between and including `(StartingExpression)` and `` as well as the four lines before `(StartingExpression)` – Gal Appelbaum Oct 27 '15 at 15:03
  • 1
    `awk` is a better alternative than `sed` for anything complicated. It doesn't have cryptic single letter codes as `sed` so you may need to type more but it's readable. – karakfa Oct 27 '15 at 15:57
  • Hi... You're going to kill me. I added another condition that I just realized I missed... Mind taking a look once more? I just need to figure out where I add this last condition in. Also, thanks a ton... you've been insanely helpful here and in other questions I've asked! – Gal Appelbaum Oct 27 '15 at 21:12
  • what does the $0 in a[p]=$0 stand for? I know that $0, $1, $2, etc are arguments... what if $0 is occupied? – Gal Appelbaum Oct 27 '15 at 22:50
  • 1
    $0 is the whole line, $1, $2, etc are the fields separated by FS. – karakfa Oct 27 '15 at 23:31
  • Ok something isn't right... to run this script I essentially run this: `./Script.sh ${CustIDInput} GET_CUSTOMER_INFO` what ends up happening is that $0 gets the following value: `./Script.sh` why's that? – Gal Appelbaum Oct 28 '15 at 09:57
  • its the file that contains the awk... I need to run it as an independent script rather than the command itself. – Gal Appelbaum Oct 28 '15 at 13:03
  • I think argument substitution is not correct in your script. Without knowing what it is difficult to diagnose. – karakfa Oct 28 '15 at 13:27
  • From my understanding, in ksh $0 = script name... is $0 different in awk? maybe I need to create an awk script that's independent of the ksh script this command is in...? – Gal Appelbaum Oct 28 '15 at 13:36
  • Hi... how would I go about to write this as an independent awk script? `#!/usr/bin/awk -f -k` and then what comes in the parenthesis? I'm still stuck trying to get an XML block with a specific value in it and the four rows above it :\ – Gal Appelbaum Nov 05 '15 at 20:07
  • 1
    there are two awk scripts there; probably easier this way. You can copy each to its own file and run it similarly piping one output to the next. – karakfa Nov 05 '15 at 21:15
  • figured it out (hopefully) thanks again for all your trouble! expect some more questions from me soon ;) (at least I know there's someone here that knows awk properly!) – Gal Appelbaum Nov 06 '15 at 19:41
0

When you have a grep version that supports -A and -B, and your file is made with fixed lines, you can use

grep -A2 -B6 "Name=Andy" request

Using your vi-knowledge can help for making the following command:

echo "/(StartingExpression)/-3,/<.Interface>/ p" | ed -s request

I used a . for the slash in , I did not want to escape the character. This will only work when Andy is the first name.

Making it working with Andy somewhere else becomes more tricky:

rm /tmp/andy
echo "/Name=Andy/
?(StartingExpression)
-3,/<.Interface>/ w /tmp/andy" | ed -s request 2>&1 >/dev/null
cat /tmp/andy
rm /tmp/andy
Walter A
  • 19,067
  • 2
  • 23
  • 43