1

Following up on my previous question about using Golang's regex to replace between strings. I now have a bit of complexity added to it. Here is what the contexts of my file looks like:

foo:
    blahblah
    MYSTRING=*
bar:
    blah
    blah
    MYSTRING=*

I need to replace what's between MYSTRING= and \n with a string of my choice (like previous stated in the original post). I can do that with:

var re = regexp.MustCompile(`(MYSTRING=).*`)
s := re.ReplaceAllString(content, `${1}stringofmychoice`)

But now I need to match and replace only after a certain occurrence. So that the contents of my file can look something like this:

foo:
    blahblah
    MYSTRING=foostring
bar:
    blah
    blah
    MYSTRING=barstring

ReplaceAllString obviously replaces everything, which is not what I want. Is there a way to only match and replace the first occurrence after a certain string?


For a bit of background about all of this. I'm trying to write a program to edit the contents of a given docker-compose.yml file and its environment variables. I need to edit the environment variable MYSTRING differently depending on what service it's listed under. In the example above, the two different services would be foo and bar.

3
  • Do you have to capture MYSTRING= and then use a backreference in the replacement pattern? Can you hardcode MYSTRING=? Commented Feb 26, 2019 at 23:41
  • Yes I can hardcore MYSTRING=. Commented Feb 26, 2019 at 23:47
  • There comes a time when regular expressions are no longer the best solution to a problem... Commented Feb 27, 2019 at 0:06

1 Answer 1

1

You may use ReplaceAllStringFunc and use a regex like

(?m)^bar:(?:\n\s{4}.*)+

See the regex demo. It will match a bar block indented with four whitespaces. Then, after a match is obtained, you may use a regular ReplaceAllString on the match.

See the Go demo:

package main

import (
    "fmt"
    "regexp"
)

const sample = `foo:
    blahblah
    MYSTRING=*
bar:
    blah
    blah
    MYSTRING=*`

func main() {
    re := regexp.MustCompile(`(?m)^bar:(?:\n\s{4}.*)+`)
    re_2 := regexp.MustCompile(`(MYSTRING=).*`)
    s := re.ReplaceAllStringFunc(sample, func(m string) string {
                return re_2.ReplaceAllString(m, `${1}stringofmychoice`)
        })
    fmt.Println(s)
}

Here, the second occurrence is changed in the bar block:

foo:
    blahblah
    MYSTRING=*
bar:
    blah
    blah
    MYSTRING=stringofmychoice
Sign up to request clarification or add additional context in comments.

4 Comments

Sorry, I think I might have worded my question a bit too confusingly. I meant to say that I need the MYSTRING=* replaced with MYSTRING=stringofmychoice after a certain string has appeared, not after it has appeared a certain number of times. In the example above, that certain string would be bar. I want to find a string, bar, and change the MYSTRING=* into MYSTRING=barstring after it.
@thestateofmay That does not change much.
Would it be possibly to greedily use the matching between bar and MYSTRING= instead? I'm trying something like re := regexp.MustCompile("(bar).(MYSTRING=).*") but it's not working.
@thestateofmay re := regexp.MustCompile("((?s:bar.*?MYSTRING=)).*"). Not sure what you mean though. If you plan to use it as a 1-regex solution, watch out. Since there is no lookahead support, it is difficult to come up with a working and readable regex for this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.