2

I can print the braceted lines in the file a

first
[
third
fourth
]
sixth
[
eighth 
]
tenth

by doing

% <a sed -n '/\[/,/\]/p' 

which prints

[
third
fourth
]
[
eighth
]

But what if I want only the second match, ie. the last three lines?

4 Answers 4

2

Easier to do with awk, assuming the blocks defined by [ and ] do not themselves contain [ or ] inside the block

$ awk -v b=2 '/\[/{c++} c==b; /]/ && c==b{exit}' ip.txt
[
eighth 
]
  • -v b=2 variable to specify which block is required
  • /\[/{c++} increment counter if line matches starting condition
  • c==b; print input record if counter is equal to block required
  • /]/ && c==b{exit} exit on matching ending condition

another way to write this:

awk -v b=2 '/\[/{c++} c==b{print $0; if(/]/) exit}' ip.txt
1
  • 1
    Separating rules with semicolon makes it more readable (missing after /\[/{c++}).
    – x-yuri
    Mar 31, 2019 at 15:18
1
$ sed -n '/^\[/h; /^\[/,/^\]/H; ${x;s/^\[\n//;p;}' file
[
eighth
]

Annotated sed script (assumes -n):

/^\[/h;         # replace hold space with this line
/^\[/,/^\]/H;   # append these lines to hold space with embedded newlines
${              # at last line of input
    x;          # swap in the hold space
    s/^\[\n//;  # delete the first "[" and the newline following it
    p;          # print
}

That is, whenever we find a line that starts with [, clear the hold space by copying the line there. Then keep appending lines to the hold space until we find the corresponding line that starts with ].

At the end, we will have a hold space with one [ too many, so delete that (and the embedded newline after it) before printing the data.

1
  • 1
    This will print the last block and not the second. In this dataset, the second block happens to be the last is why it seems to work. Aug 5, 2018 at 16:28
0

Using sed editor, we can perform it as follows:

sed -ne '                    ;# "-n" suppresses autoprint of pattern space
    /^\[/!d                  ;# skip lines that donot begin a fresh block
    :a;$!N;/\n]/!ba          ;# keep appending lines to pattern space until end of block
    G                        ;# append our makeshift counter (hold space) to pattern spc
    s/\n\{2\}$//p            ;# only when counter had 2 chars in it, we print block
    /\n$/!q                  ;# target block, 2nd block has been printed so quit 
    x;s/$/\n/;x              ;# target block not reached, so increment
' input.file

with Perl, we can use the ... operator in tandem with the boolean $k == 2, indicating that we have reached the intended target block and need to print it.

perl -lne 'print if /^\[/ && ++$k == 2 ... /^]/' input.file    
1
  • I've refactored your script a bit. Nothing major. Switched to extended regexps. Make it more strict about separators (/^\[$/). Supposedly unneeded condition before N ($!N). And not sure why you use ;# for comments.
    – x-yuri
    Mar 31, 2019 at 15:03
0
nl -bp'\[' -s ' ' input.txt | sed -n '/ *2 \[/,/\]/p' | cut -c 8-

input.txt:

first
[
third
fourth
]
sixth
[
eighth 
]
tenth

standard output:

[
eighth 
]

Explanation

  1. nl -bp can add line numbers based on specific pattern.
  2. Here we use white space as separator instead of tab by nl -s ' ' because \t is not portable in sed.
nl -bp'\[' -s ' ' input.txt
       first
     1 [
       third
       fourth
       ]
       sixth
     2 [
       eighth 
       ]
       tenth
  1. By default, nl prepend 6 characters for padding, you can remove first 7 characters by cut -c 8-.

You must log in to answer this question.

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