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.

  • 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 Feb 23 '18 at 14:24
  • thanks for adding your efforts :) am still not clear what you mean by print first portion and then next portion... – Sundeep Feb 23 '18 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 Feb 23 '18 at 14:35
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
  • if that's the output OP needs, why not a simple awk '/^\* i/ && ++c==2{print ""} 1'? ;) – Sundeep Feb 23 '18 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) – RomanPerekhrest Feb 23 '18 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 Feb 23 '18 at 14:50
  • @stacko, if there would be 2 different scripts each outputting a separate section - why caring of "delete unnecessary part"? – RomanPerekhrest Feb 23 '18 at 14:55
  • 1
    @stacko, here you are, see my update – RomanPerekhrest Feb 23 '18 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.