Find and replace text with all-inclusive wild card

2019-09-14 14:56发布

问题:

I have a file like this:

foo and more
stuff
various stuff
variable number of lines
with a bar
Stuff I want to keep
More stuff I want to Keep
These line breaks are important

I want to replace everything between foo and bar so that I get:

foo testtext bar
Stuff I want to keep
More stuff I want to Keep
These line breaks are important

as recommended in another thread I tried: sed -e '/^foo/,/^bar/{/^foo/b;/^bar/{i testtext' -e 'b};d}' file.txt

Is there a more general-purpose solution to find and replace everything between foo and bar, absolutely no matter what it is?

回答1:

You can use the following sed script:

replace.sed:

# Check for "foo"
/\bfoo\b/    {   
    # Define a label "a"
    :a  
    # If the line does not contain "bar"
    /\bbar\b/!{
        # Get the next line of input and append
        # it to the pattern buffer
        N
        # Branch back to label "a"
        ba
    }   
    # Replace everything between foo and bar
    s/\(\bfoo\)\b.*\b\(bar\b\)/\1TEST DATA\2/
}

Call it like this:

sed -f extract.sed input.file

Output:

fooTEST DATAbar
Stuff I want to keep
More stuff I want to Keep
These line breaks are important

If you want to pass the begin and ending delimiter using a shell script you can do it like this (comments removed for brevity):

#!/bin/bash

begin="foo"
end="bar"

replacement=" Hello world "

sed -r '/\b'"$begin"'\b/{
    :a;/\b'"$end"'\b/!{
        N;ba
    }
    s/(\b'"$begin"')\b.*\b('"$end"'\b)/\1'"$replacement"'\2/
}' input.file

The above works as long as $start and $end won't contain regex special characters, to escape them properly use the following code:

#!/bin/bash

begin="foo"
end="bar"
replace=" Hello\1world "

# Escape variables to be used in regex
beginEsc=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$begin")
endEsc=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$end")
replaceEsc=$(sed 's/[&/\]/\\&/g' <<<"$replace")

sed -r '/\b'"$beginEsc"'\b/{
    :a;/\b'"$endEsc"'\b/!{
        N;ba
    }
    s/(\b'"$beginEsc"')\b.*\b('"$endEsc"'\b)/\1'"$replaceEsc"'\2/
}' input.file