Home > Software design >  How can I use bash to filter multiple patterns and one line before just only pattern?
How can I use bash to filter multiple patterns and one line before just only pattern?

Time:10-22

I have a file with multiple lines:

line1
line2
line3
line_before
line4
line5
line6

and 3 patterns:

line2
line4
line6

Expect:

line2
line_before
line4
line6

I try use grep -B 1 but it show line before any patterns.

Thank you for any help!

CodePudding user response:

With awk...

In my exemple, your file with multiple lines is named "file.txt" and the file with 3 patterns is filem.txt.

The awk filter.awk program:

#! /bin/awk -f

NR == FNR && NR != 2 {
    # First file: Save 1st en 3rd tokens
    m[$0]=1
    next
}
NR == FNR && NR == 2 {
    # First file: Save 2nd token
    b[$0]=1
    next
}

NR != FNR {
    # Second file

    # Search line in tokens
    for (e in m) {
        if ($0 == e) {
            # Print current line
            print
            old = $0
            next
        }
    }
    for (e in b) {
        if ($0 == e) {
            # Print previous line
            if (FNR > 1) print prev
            # Print current line
            print
            old = $0
            next
        }
    }
}
{
    # Save current line
    prev = $0
}

Execute like this (files order is very important):

awk -f filter.awk filem.txt file.txt

Output:

line2
line_before
line4
line6

CodePudding user response:

I am assuming you want -B1 for only -e line4. (I would phrase that as: "How can I grep for multiple patterns, including the line before only one?")

Suggestions with grep -zPo:

grep -zPo '\v\K(line[26]|\V*\vline4)(?=\v)' file

or (same regex, different syntax):

grep -zPo '(?<=\v)(line[26]|\V*\vline4)(?=\v)' file

Both output this:

line2
line_before
line4
line6

Explanation:

  • -z: consume entire file
  • -P support perl regexes
  • -o: output only matching strings (one line at a time)
  • \v: vertical whitespace character
  • \v\K or (?<=\v) : Require vertical whitespace before match, but do not include it in match
  • (?=\v): require vertical whitespace after match, but do not include it in the match
  • \V*\vline4 : for the line4 pattern, include the preceeding line (all not-vertical-whitespace-chars, a linebreak, and then the matching line -- to mimic grep -B1 line4
  • Related