Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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: me@stark.org

phone model: S2
name: tara
dob: 1ad
shirt color: red
physical address: Westros
email id: me@stark.org

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?

share|improve this question
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 '13 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. –  Brandon Invergo Jul 18 '13 at 9:43
add comment

4 Answers

up vote 3 down vote accepted

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

Just replace PATTERN with whatever you want.

share|improve this answer
    
Great edit @Volodymyr –  Raystafarian Jul 21 '13 at 20:59
add comment

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: me@stark.org
---
name: tara
dob: 1ad
phone model: S2
shirt color: red
physical address: Westros
email id: me@stark.org

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.

share|improve this answer
add comment

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.

share|improve this answer
1  
The record keys are unpredictable. –  Ярослав Рахматуллин Jul 18 '13 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 '13 at 23:38
    
@Tim: My example suggests that it should always start with 'name' - that's not the case... I'll update my example. –  Utkarsh Sinha Jul 19 '13 at 5:39
add comment

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: me@stark.org

name: tara
dob: 1ad
phone model: S2
shirt color: red
physical address: Westros
email id: me@stark.org
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.