2

I am trying to find a way to grep/awk multiple patterns and then print a certain number of lines below the first pattern and a certain number of lines below the second pattern. For example

  ....
  other lines
  ....
###Pattern 1####
line 1
line 2
line 3 
 ....
 other lines
 ....
####Pattern 2####
line 1
line 2
line 3 
line 4
 ....
 other lines
 ....

So what I want to do is find the two patterns in the file and print out the first pattern and the 3 preceding lines underneath, then the second pattern and the four preceding lines underneath the second pattern.

So that my desired output looks like:

####Pattern 1####
line 1
line 2
line 3
####Pattern 2####
line 1
line 2
line 3 
line 4

Updated Desired Output So before I wasn't as clear I apologize, there may be multiple patterns. So what I would hope to achieve is an output that looks like the following:

####Pattern 1####
line 1
line 2
line 3
####Pattern 2####
line 1
line 2
line 3 
line 4
-----
####Pattern 1####
line 1
line 2
line 3
####Pattern 2####
line 1
line 2
line 3 
line 4
-----
 and so on

2 Answers 2

2
sed -ne'/pattern1/{:1' -e'$p;N;s/\n/&/[num]; to' -eb1 -e\} \
     -e'/pattern2/{:2' -e'$p;N;s/\n/&/[num]; to' -eb2 -e\} \
     -ed -e:o -eh      -e'y/\n-/-\n/;s/[^-]*//g' -e'H;x;p'

That will print as many -dashes following a match block as there are newlines which follow that match. And so, if you wanted 4 lines after every pattern1 match and 2 after every pattern2 match, you'd get an additional divider line printed between each block of 4 hyphens at the tail of a pattern1 block and 2 hyphens at the tail of a pattern2 block. This is all true except when the last line is encountered while gathering the tail for each - in that case everything between the last line and the pattern match is printed, but no hyphens are appended.

You might notice that much of the code in the above sed script is fairly redundant. Basically we're just implementing an identical type of loop for each possible match. It is thanks to sed's very simple syntax rules, as is evinced above, that makes scripting sed so eminently scriptable. In other words, because sed's syntax is basic, it is a simple affair to write a script which can write a sed script.

For example, this task might easily be parameterized to work with any number of patterns and associated follow line counts:

amatch(){  sed ${2:+"-ne$(n=0;                   \
    while  [ "$#" -gt "$((!!(n+=1)))" ];         \
    do     printf "\n/%s/{:$n\n\t%s;to\n\t%s\n}" \
                  "$1" "\$p;N;s/\n/&/$2" "b$n";  \
           shift 2;                              \
    done;  printf "\nd;:o\n\t%s\n\t%s\n\tH;x;p"  \
                  'h;y/\n-/-\n/' 's/[^-]*//g'    \
)"}; }

So long as amatch() is called with 2 or more parameters, it will build out a sed script just like the one above, and write a loop for each pair.

So it first builds and prints a sed script in a subshell, and afterward sed runs it against stdin.

So when I do:

seq 30 | amatch \[45] 5 1$ 2

The shell's while loop assembles and prints out to the command substitution a script that looks like:

/[45]/{:1
    $p;N;s/\n/&/5;to
    b1
}
/1$/{:2
    $p;N;s/\n/&/2;to
    b2
}
d;:o
    h;y/\n-/-\n/
    s/[^-]*//g
    H;x;p

And sed evaluates that against stdin and prints...

1
2
3
--
4
5
6
7
8
9
-----
11
12
13
--
14
15
16
17
18
19
-----
21
22
23
--
24
25
26
27
28
29
-----
4
  • what do the N and num represent in this part of the sed command: -e'$p;N;s/\n/&/[num];?
    – pdm
    Jul 8, 2015 at 15:11
  • @pdm - N is the sed command for appending the Next line to pattern-space, as delimited by a \newline character. Each time we do N our pattern-space \newline count is incremented by one. So we loop over it until we have [num] \newlines. The [num] as written is a syntax error - the intention is that you should replace that with an actual number. Or else use amatch().
    – mikeserv
    Jul 8, 2015 at 15:28
  • Is there anything else I would need to add to this sed command other than the value for num? I tried running it and it doesn't seem to be working.
    – pdm
    Jul 8, 2015 at 17:38
  • @pdm - you would have to change the values for pattern[12] - but why not just use the shell function? You can just copy+paste it into your terminal, and afterward just run it as amatch pattern count
    – mikeserv
    Jul 8, 2015 at 18:03
1

Doubtless some more elegant approaches, but here's one way

# awk '/Pattern 1/{for(c=0;c<4;c++){print;getline}}/Pattern 2/{for(c=0;c<5;c++){print;getline}}' foo
###Pattern 1####
line 1
line 2
line 3
####Pattern 2####
line 1
line 2
line 3
line 4
#

Marginally more elegant:

# awk '/Pattern 1/{c=4}/Pattern 2/{c=5}{while(c-->0){print;getline}}' foo
###Pattern 1####
line 1
line 2
line 3
####Pattern 2####
line 1
line 2
line 3
line 4
#

And to pop 5 hyphens under the matched set, try this

# awk '/Pattern 1/{c=4}/Pattern 2/{c=5}{while(c-->0){print;getline;x=1}if(x){print "-----";x=0}}' foo
###Pattern 1####
line 1
line 2
line 3
-----
####Pattern 2####
line 1
line 2
line 3
line 4
-----
#

Or for a "-----" at the very end use:

# awk '/Pattern 1/{c=4}/Pattern 2/{c=5}{while(c-->0){print;getline}}END{print "-----"}' foo
###Pattern 1####
line 1
line 2
line 3
####Pattern 2####
line 1
line 2
line 3
line 4
-----
#
5
  • Is there a way so that it can then print out 5 hyphens at the bottom of line 4 of pattern 2 after the while loop is done?
    – pdm
    Jul 7, 2015 at 19:40
  • Thank you very much for your help @steve, but I can seem to get the last option to work. The 5 hyphens at the end of each pattern works.
    – pdm
    Jul 7, 2015 at 20:00
  • Added example of that final approach that uses END. If failing to work for you, paste your command+output?
    – steve
    Jul 7, 2015 at 20:26
  • I used exactly what you had but I think the problem is there are multiple pattern 1 and pattern 2 and what I think is happening is the END{print "----"} is just printing that at the bottom of the output. I'll update a new desired output.
    – pdm
    Jul 8, 2015 at 0:25
  • Please remove the shell prompt when posting shell code/commands. Thanks ! Aug 23, 2015 at 1:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .