2

I'd like to print both everything before the 2nd line that begins with * i (asterisk, space, lowercase letter I) excluding the line and everything after that line including the line separately.

For example, if I had a text file like this:

* misc: go to the park
with your dog 
* important: sell badges
the bigger the pricier
24 left right now
* important: go to the mall
get clothes
* important: finish homework 

I'd like to print this first:

* misc: go to the park
with your dog
* important: sell badges
the bigger the pricier
24 left right now

and then this:

* important: go to the mall
get clothes
* important: finish homework 

How can I do this using sed?

I tried

sed  '/\* [^i]/,$   { /\* [^i]/,$ d}' /path/to/txt/

but it just prints everything before the 1st matching line.

I'd like to have two separate scripts for extracting each part.

3
  • 1
    you want those two outputs in separate files? also, please add what efforts you've made to solve this.. and, does it have to be with sed only?
    – Sundeep
    Commented Feb 23, 2018 at 14:24
  • thanks for adding your efforts :) am still not clear what you mean by print first portion and then next portion...
    – Sundeep
    Commented Feb 23, 2018 at 14:34
  • Yes. Outputting to stdout is also okay. Sed is preferred but if it's not possible, I can use other commands too.
    – stacko
    Commented Feb 23, 2018 at 14:35

2 Answers 2

2

I would suggest awk processing (which, I believe, will be more flexible and robust):

awk '/^\* i/ && ++c == 2{ print r ORS; r=""; c=0 }
     { r=(r? r ORS:"")$0 }
     END{ print r }' file

The output:

* misc: go to the park
with your dog 
* important: sell badges
the bigger the pricier
24 left right now

* important: go to the mall
get clothes
* important: finish homework

For your current simple case (without additional logic) - it may be shortened to the following:

awk '/^\* i/ && ++c == 2{ print "" }1' file

To extract the needed part separately - still with a single awk command but using dynamic argument part which accepts conditional value either 1 (1st section, preceding section) or 2 (2nd section, following section).

Scheme:

awk -v part=[12] '/^\* i/ && ++c == 2{ if (part == 1) exit; else f=1 } part == 1 || (part == 2 && f)' FILE

Usage:

-- print "before" section:

$ awk -v part=1 '/^\* i/ && ++c==2{ if (part==1) exit; else f=1 }
>         part==1 || (part==2 && f)' file
* misc: go to the park
with your dog 
* important: sell badges
the bigger the pricier
24 left right now

-- print "after" section:

$ awk -v part=2 '/^\* i/ && ++c==2{ if (part==1) exit; else f=1 }
>         part==1 || (part==2 && f)' file
* important: go to the mall
get clothes
* important: finish homework
7
  • if that's the output OP needs, why not a simple awk '/^\* i/ && ++c==2{print ""} 1'? ;)
    – Sundeep
    Commented Feb 23, 2018 at 14:38
  • 1
    @Sundeep, you have my edit. The former approach will allow adding additional logic to the captured lines (or while capturing each section) Commented Feb 23, 2018 at 14:40
  • @RomanPerekhrest Oh, that's a bit different than what I wanted. I want to run one program to show everything before the matching line and the other for everything after that, but how can I delete unnecessary part instead of inserting an empty line after that?
    – stacko
    Commented Feb 23, 2018 at 14:50
  • @stacko, if there would be 2 different scripts each outputting a separate section - why caring of "delete unnecessary part"? Commented Feb 23, 2018 at 14:55
  • 1
    @stacko, here you are, see my update Commented Feb 23, 2018 at 15:30
0

You can use sed too, It's more complicated than awk

cat sedscript.sh

deb=$1
sed 's/.*/'"$deb"'\n&/
:A
/^1/{
N
/\n\* i/!bA
:B
N
/\n\* i[^\n]*$/!bB
s/\n[^\n]*$//
s/[^\n]*\n//
q
}
:C
/^2/{
N
/\n\* i/!bC
:D
N
/\n\* i[^\n]*$/!bD
s/.*\n//
:E
N
bE
q
}
d
' $2

Call it like that :

for the first part

./sedscript.sh 1 infile

for the second part

./sedscript.sh 2 infile

You must log in to answer this question.

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