How to grep a specific pattern before match?

Viewed 286

I'm currently working on multiple configuration files which use the following format:

[Stanza1]
action.script=1
action.ping=0
action.lookup=1
action.notable.param=0
action.script.filename=script.pl
[Stanza2]
action.script=0
action.ping=0
action.lookup=1
[Stanza3]
action.script=1
action.ping=0
action.lookup=0
action.script.filename=script.pl

I want to know which stanzas include "action.script.filename=script.pl", so the expected result would be

[Stanza1]
[Stanza3]

Using something like:

grep -B 10 "action.script.filename = script.pl" file

doesn't work for cases where the stanza name is more than 10 lines before the match, and proves quite cumbersome to use.

Any suggestions on how to do this?

5 Answers

The following sed command would do the trick :

sed -n '/^\[/h;/^action\.script\.filename=script\.pl$/{x;p}'

You can try it here.

When it encounters a line that starts with "[", it stores it into its hold buffer. When it encounters a "action.script.filename=script.pl" line, it prints the content of the hold buffer.

I'm not sure this can be done purely with grep. I would recommend a small bash script:

while read line
do
    if [[ $line =~ \[.* ]]; then
        # save stanza for later
        stanza=$line
    fi
    if [[ $line =~ action.script.filename=script.pl ]]; then
        echo $stanza
    fi
done < file

With awk

$ awk '/action\.script\.filename=script\.pl/{print h} /^\[/{h=$0}' ip.txt
[Stanza1]
[Stanza3]
  • /^\[/ lines starting with [ character, you can also use something like /Stanza/ as long as it uniquely identifies header lines
    • h=$0 for such lines, save the content ($0) to variable h
  • /action\.script\.filename=script\.pl/ if input line matches the given search criteria
    • print h print the value of h variable
    • if you are matching whole line, then you can also use string match $0 == "action.script.filename=script.pl" instead of regex match

This line of code works for me

grep '^\[Stanza\|^action.script.filename=script.pl$' fileName | grep -B1 'action.script.filename=script.pl' | grep -v 'action.script.filename=script.pl\|\-\-'

Explanation:

grep '^\[Stanza\|^action.script.filename=script.pl$' fileName

matches either [Stanza]* lines or action.script.filename=script.pl ones. Output is something like this

[Stanza1]
action.script.filename=script.pl
[Stanza2]
[Stanza3]
action.script.filename=script.pl

Adding this filter | grep -B1 'action.script.filename=script.pl' will result in this

[Stanza1]
action.script.filename=script.pl
--
[Stanza3]
action.script.filename=script.pl

Now you just need to clean the output from unwanted parts

| grep -v 'action.script.filename=script.pl\|\-\-'

This is the final output

[Stanza1]
[Stanza3]
    awk '/^\[.*\]$/{stanza=$0;next} /action.script.filename=script.pl/{print stanza}' filename
    [Stanza1]
    [Stanza3]

You can store each stanza in a variable called stanza and move to next line. Whenever you see the string action.script.filename=script.pl , print the variable stanza.

Related