tags:

views:

129

answers:

3

One can use a command such as the following to substitute the word "before" to the word "after" where "before" occurs between the pair of words "begin" and "end":

sed '/begin/,/end/ {s/before/after/g}'

I am looking for a way to substitute "before" by "after" only if they do not occur inside a pair of "begin" and "end".

A: 

The right tool for that job is awk rather than sed. To do the same thing as your current sed:

echo 'before
begin
before
end
before' | awk '
    BEGIN {st=0;}
    /begin/ {st=1;}
    /end/ {st=0;}
    {
        if (st==1) {
            gsub("before","after");
            print;
        } else {
            print
        }
    }'

will give you:

before
begin
after
end
before

If you reverse the sense of the if statement controlling the gsub, you will get what you want:

echo 'before
begin
before
end
before' | awk '
    BEGIN {st=0;}
    /begin/ {st=1;}
    /end/ {st=0;}
    {
        if (st==0) {
            gsub("before","after");
            print;
        } else {
            print
        }
    }'

will give you:

after
begin
before
end
after
paxdiablo
-1 because the sed version is completely trivial, and more appropriate than awk
William Pursell
+1  A: 

The inversion is almost trivial:

sed '/begin/,/end/ !s/before/after/g'

Be aware that given the following input

begin before end
before
end
before
begin
before end
before
end
before

Your original script gives the output:

begin after end
after
end
before
begin
after end
before
end
before

Note that the 'end' on the same line as the 'begin' does not turn off the substitution. (And if you use 'end before' after a 'begin', the 'before' is still substituted by 'after'.)

Jonathan Leffler
There is a superflous 's' at the start of the address range.
William Pursell
@William - you're so right; fixed, and thanks!
Jonathan Leffler
A: 
awk '/begin/{f=1}/end/{f=0}!f{ gsub("before","after")}1' file
ghostdog74