Reputation: 654
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
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 ofRS
shall be the input record separator; a <newline> by default. IfRS
contains more than one character, the results are unspecified. IfRS
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 ofFS
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:
/age: 400/
contains that the requested line.age:40
or age : 40
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
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
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
name
, store the name in n
age
and age is 40
print n
Upvotes: 2
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
Reputation: 17258
awk -F':' '/^name/{name=$2} \
/^age/{if ($NF==40)print name}' input_file
Upvotes: 0