2

I have several text files with this format:

name: john
address: bay area
phone: 6505561234
image: /work/myimage.png

name: stark
dob: 5AD
shirt color: red
physical address: Westros
phone model: S2
email id: [email protected]

phone model: S2
name: tara
dob: 1ad
shirt color: red
physical address: Westros
email id: [email protected]

There can be multiple 'person's or 'contact's. Let's say I want to find all persons with 'S2' phone model.

I can do a 'grep' - that would just return this:

phone model: S2
phone model: S2

I can use the before/after context on grep - but that's just a fixed number of lines before/after that get printed out. With a 'previous' context of 3, I might get something like this:

shirt color: red
physical address: Westros
phone model: S2
---
name: tara
dob: 1ad
phone model: S2

But that's not what I want. I want the entire 'record' to show up. Any clues on how to go about doing this with standard unix commands?

2
  • 2
    Sounds like a job for awk, but honestly it'd be easier to do this with other tools. Why do you need standard Unix tools? Can you use a scripting language like Ruby or Python as well? Those often come with all sorts of Linux distros.
    – slhck
    Jul 18, 2013 at 8:04
  • 1
    I know you want standard Unix commands, but I'll just point out that Recutils allows you to make complex queries against data formatted in exactly this way.
    – user235731
    Jul 18, 2013 at 9:43

5 Answers 5

5

awk 'BEGIN {RS="\n\n"} $0 ~ /PATTERN/ {print $0"\n---"}' record

Just replace PATTERN with whatever you want.

0
2

If you replace your empty lines with ---, your document is a valid YAML file. This nicely represents a record structure like you have.

perl -p -e 's/^$/---/g' input > test.yml

Then, to get the complete records:

ruby -e 'require "yaml"; 
YAML::load_documents(File.open("test.yml")) do |d| 
  puts d.to_yaml if d["phone model"] == "S2" rescue nil
end'

This prints:

---
name: stark
dob: 5AD
shirt color: red
physical address: Westros
phone model: S2
email id: [email protected]
---
name: tara
dob: 1ad
phone model: S2
shirt color: red
physical address: Westros
email id: [email protected]

Marked as CW since you were asking for standard Unix tools, which ruby obviously isn't. Works with Ruby 1.8.7, 1.9.3 and 2.0.0.

0

Using this question I manages to find a way to do a multiline grep for the blocks:

grep -Pzo 'name:(..*\n)*phone model:.*S2.*(..*\n)*' filename \
 | sed 's/^name:/---\nname:/' \
 | tail -n +2

where (..*\n)* is any number of contagious non-empty lines. -P enables perl syntax, -z allows newline searches and -o prints only the matches. Sed is used for separation and tail cuts the first line.

3
  • 1
    The record keys are unpredictable. Jul 18, 2013 at 18:47
  • @Ярослав The assumption I made was that all records start with the name key. From the example I expected this format to mean 1. all records start with the key name 2. all keys are written on their own line 3. records are separated by an empty line. My solution works fine for that.
    – Tim
    Jul 18, 2013 at 23:38
  • @Tim: My example suggests that it should always start with 'name' - that's not the case... I'll update my example. Jul 19, 2013 at 5:39
0

The solution with awk is pretty neat. Here is a possible solution with sed:

  1. -n do not print anything
  2. all non-empty lines are appended to the hold register
  3. when an empty line is encountered, the hold register and pattern space are swapped
    • if the pattern space contains the string passed in as the first argument to look4pattern(), the whole set of lines is printed.
  4. same as 3. for the last line (can't know if last line is empty)

$ look4pattern() { 
    sed -n '
    /^$/!{H}; 
    /^$/{ x ; /'$1'/p }; 
       ${ x ; /'$1'/p }
  ' < records.txt
  }

$ look4pattern S2

name: stark
dob: 5AD
shirt color: red
physical address: Westros
phone model: S2
email id: [email protected]

name: tara
dob: 1ad
phone model: S2
shirt color: red
physical address: Westros
email id: [email protected]
0

Records separated by blank lines is actually a common format for fortune files, and (at least in the version of fortune I have, fortune-mod on Arch Linux) there is a -m option that prints all fortunes (records) matching a regex. So something like this:

fortune -m "model: S2"

and it will print all your records.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .