12

I have a text file which contains multiple sections and I want to print one of those sections.

Part of the file looks like

3. line 3
4. line 4

## Screenshots ##

1. line 1
2. line 2
3. line 3
4. line 4

## Changelog ##

3. line 3
4. line 4

From this I want to retrieve all lines between ## Screenshots ## and the starting of the next section. Here the next section is ## Changelog ##, but it could be anything. So the only thing which we can depend on is that it will start with ##.

From another thread, I found the following code

sed -e "H;/${pattern}/h" -e '$g;$!d' $file

which I modified to

sed -e "H;/## Screenshots ##/h" -e '$g;$!d' readme.md

Now, it retrieves all lines starting from ## Screenshots ##, but it prints all lines till the end of the file.

I then piped it to another sed like

sed -e "H;/## Screenshots ##/h" -e '$g;$!d' readme.md | sed "/^##/q" 

But now it prints only

## Screenshots ##

Is there anyway I can print all lines in the screenshots section?

Community
  • 1
  • 1
Sudar
  • 18,954
  • 30
  • 85
  • 131

4 Answers4

27
awk '/pattern/{p=1;print;next} p&&/^##/{p=0};p' file

take the "Screenshot" as example:

kent$  awk '/^## Screenshot/{p=1;print;next} p&&/^##/{p=0};p' file
## Screenshots ##

1. line 1
2. line 2
3. line 3
4. line 4

EDIT add explanation

awk '/^## Screenshot/{p=1;print;next} : if match pattern, set p=1,print the line,read next line,(stop processing following scripts)
p&&/^##/{p=0}                         : if p==1 and match /##/ again (next section), set p=0
;p' file                              : if p==1, print the line

sed only

sed -n '/## Screensh/,/##/{/Scree/{p;n};/##/{q};p}' file

EDIT2 add explanation to sed cmd

-n                 -> not print
'/## Screen/, /##/ -> match range, I guess you knew it already
{                  -> if in this range
    /Scree/        -> and line matches /Screenshot/
        {p;n};     -> do print line, and read next row (skip doing rest processing)
    /##/           -> if line matches "##"
        q;         -> quit, we have done all printing
    p              -> if we come to here, print the line
}
Kent
  • 189,393
  • 32
  • 233
  • 301
  • Can you kindly write a brief line explaining how it works? I am able to follow part of it, but not fully. – Sudar May 16 '13 at 12:43
  • Thanks. It works, but I am choosing the other answer since I already had subsequent commands in `sed`. I have upvoted the answer though :) – Sudar May 16 '13 at 13:07
  • @Sudar, ok you didn't mention the requirement of sed. or (sed only). any way, I added a sed only solution, without piping to head. – Kent May 16 '13 at 13:11
  • Nice `sed` solution. You can omit the curly braces around the `q` though. – Thor May 16 '13 at 13:19
  • +1 for avoiding the pipe :) I started writing it, but then it became something else. So i took the cheating approach :), instead of correcting the regex, just piped it to head – abasu May 16 '13 at 13:24
  • @Kent Sorry I was not clear about `sed`. I have accepted your answer now. One thing I noted is that your `sed` solution doesn't work in Mac. It works only in Ubuntu. – Sudar May 16 '13 at 16:26
  • @Sudar I am not familiar with bsd/mac sed. I have gnu sed. I think your ubuntu has installed gnu sed too. You could take a look the man page, if some options are needed for bsd sed. I have 0 experience with mac... – Kent May 16 '13 at 16:32
  • @Kent Can you kindly explain the last part of the `sed` command. I tried referring the man pages, but still couldn't figure it out. Once I understand it, I will try to port it to bsd sed. – Sudar May 17 '13 at 09:40
  • I tried run " | | in a bash script and pipe the awk output to "while read line", I was not getting any output. I ended up doing print $0 inside awk. Anyone know why this works? "while read line" works ok till grep, but breaks after awk. – Kaushik Evani Jun 14 '17 at 14:42
10

sed -n '/## Screenshots ##/,/##/p' readme.md

This will start printing from ## Screenshots ## till the next ## is found. If you don't want the last ## match, easiest is

sed -n '/## Screenshots ##/,/##/p' readme.md |head -n-1

abasu
  • 2,454
  • 19
  • 22
6

awk

This can be done more easily and more generically with awk:

awk '/^##/ { p-- } /^## Screenshots/ { p=1 } p>0' infile

If you only want one section, this will do:

awk '/^##/ { p=0 } /^## Screenshots/ { p=1 } p' infile

Output:

## Screenshots ##

1. line 1
2. line 2
3. line 3
4. line 4

Explanation

/^##/ { p-- }               # subtract one from the section counter
/^## Screenshots/ { p=1 }   # set section counter if current line has Screenshot 
p>0                         # print line if section counter greater than 0

sed

sed -n '/^## Screenshots/,/^##/p' infile | sed '$d'
Thor
  • 45,082
  • 11
  • 119
  • 130
2

I just needed to filter out sections of a file where they can repeat. I'm talking about output of dwarfdump. Each section starts with a name, like .debug_macro, or .debug_line. For example,

.debug_info
...
.debug_macro
...
.debug_line
...
.debug_macro
...

Here's the script (run with -n):

:l1 /^\.debug_macro/ {  # loop that prints a section
    # pattern space holds current line
    # hold space holds contents of current .debug_macro section
    #     up until current line
    $ { p; q }          # print and quit if at the last line
    h                   # initialize hold space (copy first line of the section)
    :l2 n               # loop that adds lines to hold space
    $ {                 # if at the last line...
        /^\./ {         # if at the beginning of a section
            /^\.debug_macro/ {   # if at the beginning of .debug_macro section
                H                # add line to hold space
            }
        }
        /^\./ ! H       # add line if not at the beginning of a section
        x; p            # print hold space
        q               # quit
    }
    /^\./ ! {           # if not at the beginning of a section...
        H               # add line to hold space
        b l2            # branch to :l2
    }
    x; p; x             # print hold space
    b l1                # branch to :l1
}

Output:

.debug_macro
...
.debug_macro
...
x-yuri
  • 16,722
  • 15
  • 114
  • 161