3

I'm trying to pipe into something that will return only the first "paragraph" or "section" separated by a blank line. I thought I could use awk or sed to get a range as per some other answers but it doesn't seem to work.

$ cat txt
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y

Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.4-0ubuntu1
Supported: 3y

$ cat txt |awk '/^Package:/,/^$/'
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y

Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.4-0ubuntu1
Supported: 3y

Should it not return only the first "section"? (as per: Grep starting from a fixed text, until the first blank line and https://www.unix.com/shell-programming-and-scripting/148692-awk-script-match-pattern-till-blank-line.html)

  • If I use grep -ve ^$ the blank lines get removed, so there's no special characters.
  • If I try to extract a different part, I get the parts from both "sections":

    $ cat txt |awk '/^Package:/,/^Version:/'
    Package: plasma-desktop
    Architecture: amd64
    Version: 4:5.12.9.1-0ubuntu0.1
    Package: plasma-desktop
    Architecture: amd64
    Version: 4:5.12.4-0ubuntu1
    
  • If I use sed -n '/^Package:/,/^$/p' or sed -n '/^Package:/,/^Version:/p' I get the same results as the equivalent awk.

How do I get awk or sed to stop after the first occurrence?

1
  • Welcome! Can you post the desired result? edit the question, don't add it in the comments. Commented Apr 7, 2020 at 18:15

3 Answers 3

13

This is exactly why awk has a paragraph mode:

$ awk -v RS= 'NR==1' file
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y

and to print the 2nd record is just the obvious change of NR==1 to NR==2:

$ awk -v RS= 'NR==2' file
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.4-0ubuntu1
Supported: 3y

Never use range expressions btw - they make code for trivial problems very slightly briefer than using a flag but then if your requirements change in the slightest require a complete rewrite or duplicate conditions. So any time you thing you might want to use /begin/,/end/ with sed or awk use /begin/{f=1} f{print} /end/{f=0} with awk instead and that gives you FAR more control on when/how to print the begin/end lines, etc.

3
  • 1
    Brilliant solution -- I never looked closely at RS before. Small extension -- if the file is big, then NR > 2 {exit} would speed things up. Commented Apr 7, 2020 at 20:00
  • @Paul_Pedant thanks. The performance enhancement to print one record would be NR==1{print; exit}.
    – Ed Morton
    Commented Apr 7, 2020 at 20:17
  • 1
    Much better than my answer, I'm keeping it only to show what was wrong with the original approach.
    – Quasímodo
    Commented Apr 7, 2020 at 21:43
2

In /begin/,/end/, the "action flags" are turned on each time a /begin/ match is found and are turned off when /end/ match is found. The boundary lines with "begin" and "end" are also printed.

The consequences to your input are (printed lines have a comment after them on the below samples):

  • With '/^Package:/,/^$/':
    Package: plasma-desktop        #TURN ON
    Architecture: amd64            #
    Version: 4:5.12.9.1-0ubuntu0.1 #
    Supported: 3y                  #
                                   #TURN OFF
    Package: plasma-desktop        #TURN ON
    Architecture: amd64            #
    Version: 4:5.12.4-0ubuntu1     #
    Supported: 3y                  #
  • With '/^Package:/,/^Version:/':
    Package: plasma-desktop        #TURN ON
    Architecture: amd64            #
    Version: 4:5.12.9.1-0ubuntu0.1 #TURN OFF
    Supported: 3y

    Package: plasma-desktop        #TURN ON
    Architecture: amd64            #
    Version: 4:5.12.4-0ubuntu1     #TURN OFF
    Supported: 3y

To print only the paragraph starting from "Package:" you can write

sed -ne '/^$/q' -e '/^Package:/,$p' file

sed quits processing the file as soon as it finds a blank line because of /^$/q.

With awk:

awk '/^$/{exit};/^Package:/,0' file
0

As commented by Quasímodo

/begin/,/end/ gets lines that match those regexes including the boundary lines. begin turns on the printing, and end turns off. The line right after your blank line turns on the printing again, because it also has Package: in it.

I realized I can use sed and change the /begin/ to 0 and it will start at the beginning. Since there is only one beginning it will only match once.

$ cat txt |sed -n '0,/^$/p'
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y
0

You must log in to answer this question.

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