Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

awk array to print line before the first match

Tags:

grep

bash

head

awk

For a block of text:

        USB2.1 Hub:

          Product ID: 0x0610

            Dell Universal Dock D6000:

              Product ID: 0x6006
              Vendor ID: 0x17e9  (DisplayLink (UK) Ltd.)
              Version: 31.27
              Serial Number: 1810132665
              Speed: Up to 5 Gb/s
              Manufacturer: DisplayLink

..on detecting the string DisplayLink (I've been using Manufacturer but in theory either should work) I'd like to return Dell Universal Dock D6000 (or specifically, the entire line between the two lines containing Product ID).

This should be doable with some awk array:

awk '/DisplayLink/ { for(i=NR-8;i<NR;i++) if(i>0) print a[i]; print; exit} {a[NR}=$0}' returned a syntax error.

Using this (admittedly ugly, inefficient) chain of tools returns the desired result: grep "Manufacturer: DisplayLink" -B7 -n | head -1 | awk -F'[ :]+' '{ $1=""; print $0 }'

like image 830
da4 Avatar asked Oct 16 '25 16:10

da4


2 Answers

Assumptions:

  • we want to match on Manufacturer: DisplayLink
  • we want to exit the script as soon as we find the match (ie, if there are multiple matches then we'll only show the first match)

Tweaking OP's current code (store all lines in memory, then print line # NR-7):

awk '
/Manufacturer: DisplayLink/ { i=NR-7; if (i>0) print a[i]; exit} 
                            { a[NR]=$0 }
' dell.dat

An alternative to OP's code that stores just the last 7 lines (via a modulo 7 index):

awk '
/Manufacturer: DisplayLink/ { i=NR-7; if (i>0) print a[i%7]; exit }
                            { a[NR%7] = $0 }
' dell.dat

Yet another alternative approach requiring two reads of the input file; first read notes the line where we find the match and then subtracts 7 (call this line_no); the second read then prints the line where FNR == line_no:

awk 'FNR==NR && /Manufacturer: DisplayLink/ { line_no = FNR-7; nextfile } 
     FNR==line_no
' dell.dat dell.dat

All of these generate:

            Dell Universal Dock D6000:
like image 129
markp-fuso Avatar answered Oct 18 '25 12:10

markp-fuso


The first method is to use the tac command to display the file on the screen in reverse from the last line to the first line, so that it can be processed normally using conventional methods (people who hope to understand can provide additional explanations)

tac file |awk '/Manufacturer: DisplayLink/{a=8}{if(--a==0)print}'

The second method is to use the grep method to compress the file into one line for processing. \N is to match non line breaks, \n is to match line breaks, and according to the meaning of the question, regular matching is sufficient

grep -Poz '\N+\n(?=(\N*\n){6}\s+Manufacturer)' file
like image 24
mrqiao001 Avatar answered Oct 18 '25 10:10

mrqiao001



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!