Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Say I have a file with information as follows:

...
Entry '234238': some text
  some text
  some text
  some text
Entry '899823': some text
  some text
  some text
Entry '234238': more text
  more text
  more text
Entry '645353': some text
  some text
  some text

I would like to extract a specific Entry '<code>'. For example, grep_my_block 'Entry '234238' should return:

Entry '234238': some text
  some text
  some text
  some text
Entry '234238': more text
  more text
  more text

Note that:

  1. The <code> that identifies a block may appear multiple times in the file. We want to extract all such blocks.
  2. Blocks may consist of an unknown number of lines

How do I do this with grep, awk or ack?

share|improve this question

4 Answers 4

up vote 5 down vote accepted
awk "/^Entry '234238'/ {printline = 1; print; next}
     /^Entry / {printline = 0}
     printline"
share|improve this answer
3  
@Gnouc I consider it bad editing practice to shorten easy to understand code to clearly less easy to understand code without adding an explanation. That's showing off and does not help the reader. –  Hauke Laging Mar 31 at 2:08
2  
Agreed. However, both versions (before and after) would benefit from some explanation... –  jasonwryan Mar 31 at 2:49
    
@HaukeLaging Agree with you. At the same time, it's worth pointing out that you could always rollback. –  devnull Mar 31 at 4:53
    
@devnull I knew that and considered it but in contrast to the comment that would not have added information. –  Hauke Laging Mar 31 at 5:05
    
@HaukeLaging Maybe rollback and leave a comment for the editor urging to refrain from making changes! –  devnull Mar 31 at 5:07
ENTRY="'234238'"
sed -n ':s;/Entry '"$ENTRY"'/{:l;p;n;/^Entry/bs;bl;}' <<\ENTRY
    Entry '234238': some text
        some text
        some text
        some text
    Entry '899823': some text
        some text
        some text
    Entry '234238': more text
        more text
        more text
    Entry '645353': some text
        some text
        some text
#END
ENTRY

OUTPUT

Entry '234238': some text
    some text
    some text
    some text
Entry '234238': more text
    more text
    more text

This should be much faster than awk (I think) due to sed's stream operations.

This was one of the less complicated of these I've done - once I wrapped my head around it. This is one of the first I managed to do without requiring GNU's extended regex - this should be pretty portable.

This branches twice - there's an achor :s at start and an anchor :l for the subscript. It works because the n operator deletes the previous line from sed's pattern-space when it pulls in a new one.

Once sed finds your "$ENTRY" it sets the branch :label, prints the line, and pulls in a new one. Then sed checks if the new line begins with the phrase 'Entry' in which case it will branch back to the :start label and begin scanning its input again for your specified "$ENTRY," else it will only branch as far as back as :label and repeat the print, next, /check/ operation.

This command boils down to the following:

until end of file do
    if current line contains "Entry $ENTRY" do
        until next line contains 'Entry' do
            print line
            delete line 
            next line
        done
    done
done
share|improve this answer

You could also use pcregrep:

pcregrep -M '234238.*(\n((?!Entry).)*)*' inputfile

This would produce all lines starting from the one containing 234238 until it encounters one that contains the word Entry.

For your sample input, it produces:

Entry '234238': some text
  some text
  some text
  some text
Entry '234238': more text
  more text
  more text
share|improve this answer
    
Would be more efficient as pcregrep -M "(?s)^Entry '234238'.*?(?=^Entry)" –  Stéphane Chazelas Mar 31 at 9:40

awk is probably a good tool to use, since the problem is line-oriented.

I'd use this variant of @HaukeLaging's solution, which has less redundancy in the code. Every line that starts with Entry clears a flag, but the header for the particular entry that you want sets the flag. If the flag is set, take the default action of printing the line.

awk "/^Entry /         { printline=0; }
     /^Entry '234238'/ { printline=1; }
     printline"
share|improve this answer

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.