5

I know the following commands will print up to the first occurrence of a certain pattern but will not include the later occurrences:

sed -n '1,/<pattern>/p' <file>

sed '/<pattern>/q' <file>

For example lets say I have a file having the following lines:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish  
this is a lion  
this is a cat

Now the outputs:

$ sed '/fish/q' file  

this is a cow  
this is a goat  
this is a some fish 

$ sed -n '1,/fish/p' file  

this is a cow  
this is a goat  
this is a some fish 

I want the output starting from the very first line till the line containing the last occurrence of fish i.e. my desired output is:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish 

How to do that using sed?

  • sed -n '/fish/p' ? – Runium Dec 2 '14 at 20:17
  • Why do you not wish to use grep for this? grep fish /path/to/file – DopeGhoti Dec 2 '14 at 21:38
  • I have edited the original question. – heemayl Dec 3 '14 at 19:26
  • OK you want to get your desired out put only using sed command. thank you for adding "I want the output to be from the first line till the line containing the last occurrence of fish", this did your question more clear. – αғsнιη Dec 3 '14 at 19:30
  • 1
    @heemayl my answer is tac infile | sed -n '/fish/,$p' |tac check if this work perfectly on your input file and you get your desired result. I will post it as an answer. – αғsнιη Dec 3 '14 at 20:16
4

Try this:

$ tac infile | sed -n '/fish/,$p' |tac

In general case if you run below sed command, you will get all line from first matched pattern to end of input file.

$ sed -n '/fish/,$p' file

this is a some fish
this is a fishie
this is a fish
this is a lion
this is a cat

So my solution is: If we run tac command on input file your last matched pattern will change to as first pattern. see the result of tac infile:

$ tac infile

this is a cat
this is a lion
this is a fish
this is a fishie
this is a some fish
this is a goat
this is a cow

The tac command is the same as cat command, while tac prints files in reverse order.

Now if we run our first sed command, you will get all lines first matched pattern to end of input file. like:

$ tac infile | sed -n '/fish/,$p'
this is a fish
this is a fishie
this is a some fish
this is a goat
this is a cow

Ok, finished. We only need to run tac command again to reverse back the lines to original order:

$ tac infile | sed -n '/fish/,$p' |tac
this is a cow
this is a goat
this is a some fish
this is a fishie
this is a fish

Done!

3

This is fairly simple - it just takes a little coordination between sed's two buffers. For example:

sed -n 'H;/fish/!d;$!n;x;p;G
' <<\INFILE
this is a cow
this is a goat
this is a some fish
this is a fishie
this is a fish
this is a lion
this is a cat
INFILE

That command appends every line to Hold space following an inserted \newline character. Any line which does !not match /fish/ is immediately thereafter deleted from output. Which leaves us with only /fish/ lines. So the line is overwritten with the next line of input. Then pattern and hold spaces are swapped - we did just Hold the line after all. Now pattern space is Hold space and vice-versa. So we just print whatever was saved from the last time a /fish/ line matched.

This can only get up to the last occurrence of a match because it only prints when it finds a match - and it stores intervening lines in the interim. Still, it only stores as little as necessary between matches - each time the buffers are exchanged they are refreshed. Here's your output:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish

Thanks very much to don cristi for showing me that I sometimes skipped a fish. It now makes certain to push the buffer to both ends each time it is refreshed - and each turn to overwrite the current pattern space before deleting it - in case. It will work with a fish on the first line or the last, and any line in between as near as I can tell.

Another thing I was doing was pulling the next line on the last line - which is a big sed no-no. Thanks again to don for helping me with that as well.

A more thorough example:

sed 'x;/./G;//!x;/fish/p;//s/.*//;x;d'

That handles a few problems surrounding the other better, I hope. Pattern/hold spaces are exchanged every cycle - and this is to avoid getting extra blank lines as a result of the edit s///ubstitution at the end of the command. So the buffers are swapped and if the hold buffer is not empty the the current line is appended into that buffer following a \newline character - which is where the extra lines would come from otherwise. Else the buffers are just swapped back and hold space remains empty for the current cycle. As near as I can tell, this command preserves all blank lines and everything else - but merely stops printing at the last match.

Some of the difficulty I had with this is commonly associated with hold space - the only way to use it effectively is to fall short of - fall behind - the line cycle in order to compare old lines with older. My usual preference is for an N;P;D loop. You might do this thing using something along those lines like...

sed -ne :n -e '/fish/!N;//p;//!bn'

There sed continuously appends the Next line of input to pattern space and branches back to the :n label to try again if fish does not match any of the lines it has built-up thus far. It only prints lines - or line-sequences - which match fish before dumping the contents at line-cycle end and starting anew with a fresh buffer.

I purposely do not test for the last line here - if the last line matches it will be printed, else - and with -n this holds true even for GNU sed - the loop will just end the file all of its own last line or no.

  • @don_crissti - correct! It's not about blank lines though - it's more about adjacent fishes. They get swallowed when they're near one another - specifically when n doesn't get checked and another fish happens in the meanwhile. I doubly populate the buffer - it will still refresh w/ every exchange and so on, but once per turn the buffer is held and got so nothing slips by. I'm about to post the edit. Thanks very much for your comment. – mikeserv Dec 3 '14 at 23:42
  • @don_crissti - please try it again if you will. I threw the stuff you mentioned at it. – mikeserv Dec 3 '14 at 23:45
  • @don_crissti - I could do that, but I don't I have that problem - I think... I'll check again. I can explicitly check the last line, of course. In any case, I do very much appreciate your help. Back in a few, I hope. – mikeserv Dec 4 '14 at 1:11
  • @don_crissti - I saw what you meant and figured out why, I think. If you find the time, try it again? I appreciate it. – mikeserv Dec 4 '14 at 1:35
  • @don_crissti - ok. fixed. I hope. I admit - you have shaken my confidence, but I was pretty busy earlier. Anyway, thanks for the test data - I verified it works at least with the file you linked me. – mikeserv Dec 4 '14 at 6:55
0

You could also use awk which might be shorter than Sed:

awk ' /^fish/ { print $0 }' filename.txt

Some people may write this:

awk ' /^fish/ { print $1 $2 $3 $4 $5 }' filename.txt

The $n represents a column. The shortcut $0 represents the entire line.

  • Please check my desired output as your answer will print the lines containing "fish" at first but i want the output to be from the first line till the line containing the last occurrence of fish. – heemayl Dec 3 '14 at 19:22
  • Fix your question stating that in words as the question is on hold. – eyoung100 Dec 3 '14 at 19:34
  • I have updated the question too.. – heemayl Dec 3 '14 at 19:43
0

The question is not very clear, but after some edit iterations I believe you should rather use pcregrep for this task. For example if the input is

this is a cow
this is a goat
this is a some fish
this is a fishie
this is a fish
this is a lion
this is a cat
this is a fisherman
this is a dog

and desire output

this is a cow
this is a goat
this is a some fish
this is a fishie
this is a fish

then

pcregrep -M '(.|\n)*fish.*(?=(.|\n)*fish.*)'

will do the job.

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.