4

Using the regexp string, how can I remove all the lines before the first line that contains a match? e.g How can I change this:

lost
load
linux
loan
linux

into this:

linux
loan
linux

I tried:

echo "lost
load
linux
loan
linux" | sed -e 's/.*^li.*$//g'

but it returns this, not changing anything:

lost
load
linux
loan
linux

I'd like to make it work so that it won't output anything when there's no match.

11

One way, POSIXly:

$ echo "lost
load
linux
loan
linux" | sed -e/linux/\{ -e:1 -en\;b1 -e\} -ed

or shorter:

sed -n '/linux/,$p'

or even shorter:

sed '/linux/,$!d'

For readers who wonder why I prefer the longer over the shorter version, the longer version will only perform i/o over the rest of file, while using ranges can affect the performance if the 2nd address is a regex, and the regexes are trying to be matched more than is necessary.

Consider:

$ time seq 1000000 | sed -ne '/^1$/{' -e:1 -en\;b1 -e\}
=====
JOB sed -e '/^1$/,$d'
87%    cpu
0.11s real
0.10s user
0.00s sys

with:

$ time seq 1000000 | sed -e '/^1$/,/1000000/d'
=====
JOB sed -e '/^1$/,/1000000/d'
96%    cpu
0.24s real
0.23s user
0.00s sys

you can see the different between two versions. With complex regex, it's will be big difference.

  • 2
    Or just sed -n '/linux/,$p' – dave_thompson_085 Jan 24 '16 at 7:01
  • @dave_thompson_085: Right, added to the answer. TIMTOWTDI – cuonglm Jan 24 '16 at 9:38
  • @cuonglm What is the stuff in the curly brackets doing? And the "-ed"? Could you explain? I thought the "-e"s were for chaining commands, but what they're linking here (:1, ;b1) don't look like commands? – stacko Jan 24 '16 at 12:26
  • @stacko: chain all the command together, you have sed -e '/linux/{:1;n;b1};d'. You can see, inside the curly brace is just a loop, execute n command till the end of file. I broke it into many pieces for POSIX compliant. – cuonglm Jan 24 '16 at 15:22
  • @ dave_thompson_085 - you should have just answered w/ that - its a good answer. i did! I think i might delete it though... I didnt see your comment before and it looks like cuonglms folded it in anyway - and its not like i offer much i else besides here... – mikeserv Jan 24 '16 at 16:18
1

Two other awk solutions:

They both just set a found flag when seeing the first regex match and print when that flag is set.

echo "lost
load
linux
loan
linux" | awk 'BEGIN {found = 0} {if (found || $0 ~ /linux/) {found = 1; print}}'

This one is a little longer but doesn't set the found flag again.

echo "lost
load
linux
loan
linux" | awk 'BEGIN {found = 0} {if (found) {print} else if ($0 ~ /linux/) {found = 1; print}}'
0

This is easy to do clearly in awk:

echo "lost
load
linux
loan
linux" | awk '
    /^li/ { found = 1 }
    found { print }'

Here found is a variable, with an arbitrarily chosen, self-explanatory name.  It gets set when the program encounters an input line that matches the regexp.  (Variables initially default to null, which is functionally equivalent to 0 or FALSE.)  So input lines are printed after the ^li pattern is matched, and not before.  The third line of the input (the first linux line) is printed because the conditional print statement comes after the statement that looks for the pattern and sets the flag.  If you want to start printing with the fourth line (the line after first linux line), just reverse the order of the two statements.

If no input line matches the regexp, the flag never gets set, and nothing is printed.

As I said, the name of the flag variable is arbitrary; you can use something shorter (e.g., f) if you want.  And { print } is the default action, so you can leave it out.  So, if you don't care about clarity, you can shorten the above to

echo "lost
load
linux
loan
linux" | awk '/^li/{f=1}f'
  • 1
    awk can also have two-part patterns where the action occurs for each group of lines starting with one that matches the left part until one that matches the right part, so awk '/linux/,0' prints lines starting with a match for linux and stopping only at EOF because 0 is false. – dave_thompson_085 Jan 24 '16 at 7:04

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.