16

I have a text file similar to this:

line 1
line 2A
line 3
line 4A
line 5

I want to "grep" from "line 2A" to the end of file, something like this

cat file.txt|some_grep "line 2A"

Also, I want to "grep" from "line 2A" to the next line that contains "A", something like this

cat file.txt| some_grep "A"

I want this to print out:

line 2A
line 3
line 4A

Which command can help me achieve this?

2
  • The first is the exact case that is trivial for awk range patterns awk '/line 2A/,0' and the second can be awk '/line 2A/,/A/&&!/line 2A/' or if it's at least one char before the A awk '/line 2A/,/[^2]A/' Jan 30, 2016 at 10:09
  • Thank you, if you could turn this into an answer, I'll mark it as it as the...answer.
    – phong
    Feb 3, 2016 at 15:49

6 Answers 6

11

(expanded from comment)

awk has a capability to select 'ranges' of lines which matches this need perfectly, as described in the the GNU-awk (gawk) manual. (This feature works in other awks but the gawk manual is easy to link.)

awk '/line 2A/,0' prints lines starting with the first one that matches line 2A and continuing until the end of input because 0 is a condition that is never true.

awk '/line 2A/,/A/&&!/line 2A/' starts printing with a line that matches line 2A and stops after a line that matches A but NOT line 2A (and thus cannot be the same line as the starting line). It will start again on a subsequent line 2A and so on; if you want to prevent that there are slightly more complicated ways to do so.

If the stopping lines always have some character other than 2 before the A this can be simplified to awk '/line 2A/,/[^2]A/' which stops after a line that matches any character other than 2, followed by A. You might want a variation of this, e.g. to stop on any-single-digit-A different from 2A, but not other As like WHAT; for that the stopping condition might be ,/line [013-9]A/.

17

I want to "grep" from "line 2A" to the end of file:

sed -n '/2A/,$p'
  • -n : suppress sed default output
  • /2A/ : output lines from the first one containing "2A"
  • $ : to end of file

I want to "grep" from "line 2A" to the next line that contains "A":

sed -n '/2A/,/A/p'
  • /A/ : output until a line contains "A"

I want to "grep" from the first line containing "A" to the next one:

printf "/A\n.+1,/A/p\nq" | ed -s

$ > foo echo "line 1
line 2A
line 3
line 4A
line 5"

$ sed -n '/2A/,$p' foo
line 2A
line 3
line 4A
line 5

$ sed -n '/2A/,/A/p' foo
line 2A
line 3
line 4A

$ printf "/A\n.+1,/A/p\nq" | ed -s foo
line 2A
line 3
line 4A
4
  • There is an hint in the question some_grep "A" which suggests OP wants smallest range of lines between two containing "A". Defining the range from /2A/ to /A/ is a specific case and a workaround rather than an answer.
    – techraf
    Jan 30, 2016 at 10:42
  • @techrat I'm answering to the question that I quoted in my reply. Granted there is a contradiction with the parameter passed. Answer updated.
    – jlliagre
    Jan 30, 2016 at 10:50
  • Unfortunately sed -n '/A/,/A/p' is a wrong answer. Try it with an input file with more than two lines containing "A".
    – techraf
    Jan 30, 2016 at 10:54
  • @techraf Indeed, answer updated again.
    – jlliagre
    Jan 30, 2016 at 11:10
5

I think the best way is to use grep in combination with cut and tail. First, use grep to get the line on which the desired string is (-n to output line number; -m 1 to stop searching after the first match):

grep -n -m 1 "somestring" filename.txt

This outputs the line number and the string itself. To cut away the string, we use cut (-f1: output first field; -d: use ":" as delimiter):

grep -n -m 1 "somestring" filename.txt | cut -f1 -d:

Next, we use the output of this command as parameter in tail. Normally, tail prints the last k lines, but using -n +k, we get tail to print from line k onwards. The total command is:

tail -n +`grep -n -m 1 "somestring" filename.txt | cut -f1 -d:` filename.txt

To output the lines until somestring use head instead of tail and -n -# instead of -n +#. You could also combine both to get the lines from one string until another.

1

If you know the size maximum number of lines in the file, you can also use grep -A NUM "line 2A" or grep --after-context=NUM "line 2A", which prints NUM lines of trailing context after matching line 2A.

0

Please try the code below -

sed -n '6,$p' infile
2
  • 3
    Can you turn this into a "teachable moment" by expanding your answer to explain the command? Thanks.
    – fixer1234
    Jan 30, 2016 at 10:05
  • 1
    This ignores conditions specified by OP in first and (even after modification) does not answer the second problem: from "line 2A" to the next line that contains "A".
    – techraf
    Jan 30, 2016 at 10:15
0

The sed method is the way to go, but what if you want to keep the top lines, but not grep them?

Here's a way:

{ head -1 file.txt ; sed 1d file.txt | grep <whatever> ;}

What's going on here?

First, we spit out the top line (head -1 file.txt) Then, we remove the top line (sed 1d file.txt) and grep just that.

The whole thing is wrapped by { .. ;}, so you can pipe or redirect both the header and the grepped body together afterward. Like this:

{ head -1 file.txt ; sed 1d file.txt | grep ;} > newfile.txt

If you want to skip the first 10 lines, change it to this

{ head -10 file.txt ; sed 1,10d file.txt | grep ;}

You must log in to answer this question.

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