Jules
Jules

Reputation: 303

How to treat multiple lines as one result in grep, or at least filter without mutating

grep -A5 -E 'L337' -E 'Blue' -E 'Honda' -E 'Height: 6'  ./vehicles

Using this bash code, I've filtered hundreds of results to 13 like the one below:

License Plate L337ZR9
Make: Honda
Color: Red
Owner: Katie Park
Height: 6'2"
Weight: 189 lbs

I'm trying to filter first by plate, and then remove any not Blue, and not Honda, and any shorter than 6'. My syntax is wrong and I can only return this, or any individual line matching any of the search terms.

Using -v has two problems, the first being that it's impractical to filter by every negative when I have exact positives to search for, and the second being that

grep -v 'Height: 5'

just removes the height line, leaving records that are both incompatible and incomplete.

How can I filter my results with grep, while retaining all 6 lines? Can I specify a line-by-line filter?

-

Edit: The file looks like this, only I've already grep-ed for 'L337' in the plate. The issue is that I'm having trouble treating 6 individual lines of data as a single 6-line record. -A5 lets me return 5 lines beneath the License Plate, but I don't know of a way to grep for 'Blue' without cutting License Plate and Make off the top.

License Plate L337DV9
Make: Honda
Color: Blue
Owner: Joe Germuska
Height: 6'2"
Weight: 164 lbs
--
License Plate L3375A9
Make: Honda
Color: Blue
Owner: Jeremy Bowers
Height: 6'1"
Weight: 204 lbs
--
License Plate L337WR9
Make: Honda
Color: Blue
Owner: Jacqui Maher
Height: 6'2"
Weight: 130 lbs

Upvotes: 0

Views: 188

Answers (4)

stack0114106
stack0114106

Reputation: 8711

You can get this easily using Perl

$ perl -0777 -ne ' @a=split("--"); @b=grep { /L337/ and /Honda/ and /Blue/ and /Height: 6/ } @a; print @b ' jules.txt
License Plate L337QE9
Make: Honda
Color: Blue
Owner: Erika Owens
Height: 6'5"
Weight: 220 lbs
$ cat jules.txt  # your file 
License Plate L337QE9
Make: Honda
Color: Blue
Owner: Erika Owens
Height: 6'5"
Weight: 220 lbs
--
--
License Plate L337GB9
Make: Toyota
Color: Blue
Owner: Matt Waite
Height: 6'1"
Weight: 190 lbs
--
--
License Plate L337OI9
Make: Jaguar
Color: Blue
Owner: Brian Boyer
Height: 6'6"
Weight: 201 lbs
--
--
$

Upvotes: 1

kvantour
kvantour

Reputation: 26481

With GNU awk you can do the following:

awk 'BEGIN{RS="\n--\n?";ORS="\n--\n"; FS="\n"}
     ($1 ~ /L337/) && ($2 ~ /Honda/) && ($3 ~ /Blue/) && ($5 ~ /[^0-9]6'/)' file

This will pick up all Blue Honda's with a license plate starting the L337 and are between 6' and 7' high.

If the fields are not always in the same order, you have to manipulate it a bit.

awk '/License Plate/ && /L337/ { f++ }
     /Make/ && /Honda/ { f++ }
     /Color/ && /Blue/ { f++ }
     /Height/ && / 6'2/ { f++ }
     { record = record=="" ? $0 : record ORS $0 }
     /^--$/ { if (f == 4) print record; f=record=""}
     END { if (f==4) print record; }

Upvotes: 1

Jules
Jules

Reputation: 303

I phrased this question poorly, but the upside is that @kvantour prompted me to realise a solution.

Given 3 lots of data like this:

License Plate L337QE9
Make: Honda
Color: Blue
Owner: Erika Owens
Height: 6'5"
Weight: 220 lbs
--
--
License Plate L337GB9
Make: Toyota
Color: Blue
Owner: Matt Waite
Height: 6'1"
Weight: 190 lbs
--
--
License Plate L337OI9
Make: Jaguar
Color: Blue
Owner: Brian Boyer
Height: 6'6"
Weight: 201 lbs
--

I want to match a plate with 'L337', that is Blue, a Honda, with an owner who is at least 6'. Or I want to remove any results that do not match all of these requirements. I do not expect to see any results with Red cars, Toyotas, or short owners, so only Erika Owens' record would be returned.

My working solution is as follows:

grep -A5 'L337' ./vehicles | grep -B1 -A4 'Honda'  | grep -B2 -A3 'Blue' | grep -B4 -A1 'Height: 6'

The discovery of -Bn allowed me to filter without losing the top lines. My thanks to everyone's contributions, and apologies for the confusion.

Upvotes: 0

RavinderSingh13
RavinderSingh13

Reputation: 133518

If I understood correctly you DO NOT want to print section which has any other color than blue(one could change color name in condition in case other than blue), then following may help.

awk '
/License Plate/ && value{
  if(flag==1){
    print value
  }
  flag=value=""
}
/Color:/ && $2=="Blue"{
  flag=1
}
{
  value=(value?value ORS $0:$0)
}
END{
  if(flag==1){
    print value
  }
}'   Input_file

Upvotes: 1

Related Questions