tags:

views:

177

answers:

3

How can I do a sed regex swap on all text that preceed a line matching a regex.

e.g. How can I do a swap like this

s/foo/bar/g

for all text that precedes the first point this regex matches:

m/baz/

I don't want to use positive/negative look ahead/behind in my sed regex, because those are really expensive operations on big files.

+4  A: 

If you mean that you want to do the substitution on every line preceding the given match, this is your answer:

The substitution takes an optional address range; you can use both numbers and patterns. In this case, start from line 1, go until your pattern:

sed '1,/baz/s/foo/bar/g'

In awk:

awk '
/baz/ { done = 1 }
{
    if (!done) {
        gsub(/foo/, "bar")
    }
    print
}'

(It's really short enough to leave out the line breaks, but they make it readable)

Jefromi
You might note that you cannot do '1,/baz/-1s/foo/bar/'.
Jonathan Leffler
@Jonathan: That's a very good point. If you want to avoid touching the line where /baz/ matched, this is suddenly very very difficult in sed - but awk would still be able to do it.
Jefromi
both your solution will change "foo" to "bar" if "baz" is not found
ghostdog74
@ghostdog74: Is that not the desired behavior? My reading of "until first match" is that if there is no match, you go until the end.
Jefromi
@ghostdog74: I don't interpret that as a requirement.
Dennis Williamson
@jef,dennis. maybe i read wrongly the requirement. my bad.
ghostdog74
+3  A: 
$ cat file
123 abc 01
456 foo 02 bar
789 ghi
baz
blah1
blah2
foo bar

$ awk -vRS="baz" 'NR==1{gsub("foo","bar")}1' ORS="baz" file
123 abc 01
456 bar 02 bar
789 ghi
baz
blah1
blah2
foo bar
baz

use "baz" record separator , then the 1st record will be the record you want to change "foo" to "bar".

with sed, variation of Denni's solution to take care of "baz" at first line

sed '0,/baz/{/baz/!s/foo/bar/g}' file
ghostdog74
+2  A: 

This variation on Jefromi's answer should do the trick of not touching the line that "baz" appears on as mentioned in Jonathan's comment.

sed '1,/baz/{/baz/!s/foo/bar/g}'
Dennis Williamson
Wow, I should have thought of that. I was already thinking about all this nonsense reading next line in... +1.
Jefromi