tags:

views:

233

answers:

11

How do I display data from the beginning of a file until the first occurrence of a regular expression?

For example, if I have a file that contains:

One
Two
Three
Bravo
Four 
Five

I want to start displaying the contents of the file starting at line 1 and stopping when I find the string "B*". So the output should look like this:

One
Two
Three
+2  A: 

in Perl:

perl -nle '/B.*/ && last; print; ' source.txt
catwalk
/B.*/ will match anything containing a B, not starting with a B.
rjh
The question didn’t state that the ‘B’ should be at the start of a line.
Ciarán Walsh
Sorry, you're right - it's up to our interpretation there.
rjh
The -p instead of the -n will automatically give you that print at the end. :)
brian d foy
Also, `-l` is unnecessary.
ephemient
+4  A: 
sed '/^B/,$d'

Read that as follows: Delete (d) all lines beginning with the first line that starts with a "B" (/^B/), up and until the last line ($).

ndim
A: 

If Perl is a possibilty, you could do something like this:

% perl -0ne 'if (/B.*/) { print $`; last }' INPUT_FILE
tomo
Seems excessive to be reading a whole file into memory before doing anything.
ephemient
+5  A: 

if its from the start of the file

awk '/^B/{exit}1' file

if you want to start from specific line number

awk '/^B/{exit}NR>=10' file # start from line 10
ghostdog74
+8  A: 

perl -pe 'last if /^B/' source.txt

An explanation: the -p switch adds a loop around the code, turning it into this:

while ( <> ) {
    last if /^B.*/;  # The bit we provide
    print;
}

The last keyword exits the surrounding loop immediately if the condition holds - in this case, /^B/, which indicates that the line begins with a B.

rjh
+4  A: 
sed -n '1,/^B/p'

Print from line 1 to /^B/ (inclusive). -n suppresses default echo.


Update: Opps.... didn't want "Bravo", so instead the reverse action is needed ;-)

sed -n '/^B/,$!p'

/I3az/

draegtun
Unfortunately, this prints including the "Bravo" line, while the question wants to exclude the "Bravo" line.
ndim
Arghh!... didn't read question correctly. Updated with reverse action "!p" (ie. print!) for everything not between /^B/ and end of file. Many thanks for spotting this ndim.
draegtun
Ah. `!` modifies an address range such that it only matches where it would not match, and vice versa. It is still part of the address range. Well, `!` was new to me... thanks for pointing me to it.
ndim
+1  A: 

Here is a perl one-liner:

perl -pe 'last if /B/' file
jmcnamara
+1  A: 

Just sharing some answers I've received:

Print data starting at the first line, and continue until we find a match to the regex, then stop:

<command> | perl -n -e 'print "$_" if 1 ... /<regex>/;'

Print data starting at the first line, and continue until we find a match to the regex, BUT don't display the line that matches the regular expression:

<command> | perl -pe '/<regex>/ && exit;'

Doing it in sed:

<command> | sed -n '1,/<regex>/p'
Pete
The flip flop operator is one of my favorites, and this situation is one of the few times I get to use it. :)
brian d foy
+1  A: 

Some of the sed commands given by others will continue to unnecessarily process the input after the regex is found which could be quite slow for large input. This quits when the regex is found:

sed -n '/^Bravo/q;p'
Dennis Williamson
+2  A: 

Your problem is a variation on an answer in perlfaq6: How can I pull out lines between two patterns that are themselves on different lines?.


You can use Perl's somewhat exotic .. operator (documented in perlop):

perl -ne 'print if /START/ .. /END/' file1 file2 ...

If you wanted text and not lines, you would use

perl -0777 -ne 'print "$1\n" while /START(.*?)END/gs' file1 file2 ...

But if you want nested occurrences of START through END, you'll run up against the problem described in the question in this section on matching balanced text.

Here's another example of using ..:

while (<>) {
 $in_header =   1  .. /^$/;
 $in_body   = /^$/ .. eof;
# now choose between them
} continue {
 $. = 0 if eof; # fix $.
}
brian d foy
A: 

one liner with basic shell commands:

head -`grep -n B file|head -1|cut -f1 -d":"` file
Vijay Sarathi