blu10
blu10

Reputation: 654

Printing out a particular row based on condition in another row

apologies if this really basic stuff but i just started with awk so i have an input file im piping into awk like below. format never changes (like below)

name: Jim
gender: male
age: 40

name: Joe
gender: female
age: 36

name: frank
gender: Male
age: 40

I'm trying to list all names where age is 40

I can find them like so awk '$2 == "40" {print $2 }'

but cant figure out how to print the name

Upvotes: 1

Views: 125

Answers (5)

kvantour
kvantour

Reputation: 26471

Awk knows the concept records and fields.

Files are split in records where consecutive records are split by the record separator RS. Each record is split in fields, where consecutive fields are split by the field separator FS.

By default, the record separator RS is set to be the <newline> character (\n) and thus each record is a line. The record separator has the following definition:

RS: The first character of the string value of RS shall be the input record separator; a <newline> by default. If RS contains more than one character, the results are unspecified. If RS is null, then records are separated by sequences consisting of a <newline> plus one or more blank lines, leading or trailing blank lines shall not result in empty records at the beginning or end of the input, and a <newline> shall always be a field separator, no matter what the value of FS is.

So with the file format you give, we can define the records based on RS="".

So based on this, we can immediately list all records who have the line age: 40

$ awk 'BEGIN{RS="";ORS="\n\n"}/age: 40/

There are a couple of problems with the above line:

  • What if we have a person that is 400 yr old, he will be listed because the line /age: 400/ contains that the requested line.
  • What if we have a record with a typo stating age:40 or age : 40
  • What if our record has a line stating wage: 40 USD/min

To solve most of these problems, it is easier to work with well-defined fields in the record and build the key-value-pairs per record:

key       value
---------------
name   => Jim
gender => male
age    => 40

and then, we can use this to select the requested information:

$ awk 'BEGIN{RS="";FS="\n"}
       # build the record
       { delete rec; 
         for(i=1;i<=NF;++i) {
            # find the first ":" and select key and value as substrings
            j=index($i,":"); key=substr($i,1,j-1); value=substr($i,j+1)
            # remove potential spaces from front and back
            gsub(/(^[[:blank:]]*|[[:blank:]]$)/,key)
            gsub(/(^[[:blank:]]*|[[:blank:]]$)/,value)
            # store key-value pair
            rec[key] = value
         }
       }
       # select requested information and print
       (rec["age"] == 40) { print rec["name"] }' file

This is not a one-liner, but it is robust. Furthermore, this method is fairly flexible and adaptable to make selections based on a more complex logic.

Upvotes: 2

RavinderSingh13
RavinderSingh13

Reputation: 133428

Could you please try following(I am driving as of now so couldn't test it).

awk '/^age/{if($NF==40){print val};val="";next}  /^name/{val=$0}'  Input_file

Explanation: 1st condition checking ^name if a line starts from it then store that line value in variable val. Then in other condition checking if a line starts from age; then checking uf that line's 2nd field is greater than 40 then print value if variable val and nullify it too.

Upvotes: 2

Jotne
Jotne

Reputation: 41446

Using gnu awk and set Record Selector to nothing makes it works with blocks.

awk -v RS="" '/age: 40/ {print $2}' file
Jim
frank

Some shorter awk versions of suspectus and RavinderSingh13 post

awk '/^name/{n=$2} /^age/ && $NF==40 {print n}' file
awk '/^name/{n=$2} /^age: 40/ {print n}' file
Jim
frank
  • If line starts with name, store the name in n
  • IF line starts with age and age is 40 print n

Upvotes: 2

Paul Dawson
Paul Dawson

Reputation: 1382

If you are not averse to using grep and the format is always the same:

cat filename | grep -B2 "age: 40" | grep -oP "(?<=name: ).*"
Jim
frank

Upvotes: 1

suspectus
suspectus

Reputation: 17258

awk -F':' '/^name/{name=$2} \
    /^age/{if ($NF==40)print name}' input_file

Upvotes: 0

Related Questions