Regex Replace within Sub Match - regex

Given a string (a line in a log file):
Date=2017-06-29 03:10:01.140 -700 PDT,clientDataRate="12.0,18.0,24.0,36.0,48.0,54.0",host=superawesomehost.foo,foo=bar
I'd like to replace the commas with a single space, but only within double quotes.
Desired result:
Date=2017-06-29 03:10:01.140 -700 PDT,clientDataRate="12.0 18.0 24.0 36.0 48.0 54.0",host=superawesomehost.foo,foo=bar
I've begun with a basic combination of regex and ReplaceAllString but am rapidly realizing I don't understand how to implement the match group (?) needed to accomplish this.
package main
import (
"fmt"
"log"
"regexp"
)
func main() {
logLine := "Date=2017-06-29 03:10:01.140 -700 PDT,clientDataRate=\"12.0,18.0,24.0,36.0,48.0,54.0\",host=superawesomehost.foo,foo=bar"
fmt.Println("logLine: ", logLine)
reg, err := regexp.Compile("[^A-Za-z0-9=\"-:]+")
if err != nil {
log.Fatal(err)
}
repairedLogLine := reg.ReplaceAllString(logLine, ",")
fmt.Println("repairedLogLine:", repairedLogLine )
}
All help is much appreciated.

You'll want to use Regexp.ReplaceAllStringFunc, which allows you to use a function result as the replacement of a substring:
package main
import (
"fmt"
"log"
"regexp"
"strings"
)
func main() {
logLine := `Date=2017-06-29 03:10:01.140 -700 PDT,clientDataRate="12.0,18.0,24.0,36.0,48.0,54.0",host=superawesomehost.foo,foo=bar`
fmt.Println("logLine: ", logLine)
reg, err := regexp.Compile(`"([^"]*)"`)
if err != nil {
log.Fatal(err)
}
repairedLogLine := reg.ReplaceAllStringFunc(logLine, func(entry string) string {
return strings.Replace(entry, ",", " ", -1)
})
fmt.Println("repairedLogLine:", repairedLogLine)
}
https://play.golang.org/p/BsZxcrrvaR

Related

GO AWS SDKv2: Parsing json file and adding values as tags for AWS Secretsmanager

I have the requirement to write a cross-platform cli tool in go by leveraging the AWS SDKv2 GO.
I need to parse a json file like this:
{
"Tags": [
{
"Key": "global1",
"Value": "val1"
},
{
"Key": "global2",
"Value": "val2"
}
]
}
I have this function:
package lib
import (
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"encoding/json"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"io/ioutil"
"os"
)
//func CreateSecret(client *secretsmanager.Client, secretName string, description string, kmsKeyId string, secretString string) {
func CreateSecret(client *secretsmanager.Client) {
// Parse tags.json
jsonFile, err := os.Open("tags.json")
if err != nil {
fmt.Println(err)
}
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
tags := []*secretsmanager.Tag{}
json.Unmarshal(byteValue, &tags)
// rest of the code is truncated...
}
However, I receive this error:
lib/commands.go:58:28: undefined: secretsmanager.Tag
What am I doing wrong?
secretsmanager.Tag does not exist. But what I think you are looking for is this package: github.com/aws/aws-sdk-go-v2/service/secretsmanager/types. Import it and then you will have types.Tag, which is what you probably need.

Simplify template usage by removing array

I am trying to simplify the template which I use to make it use a flatter data structure:
from
data := []App{App{"test data", []string{"app1", "app2", "app3"}}}
To:
data := App{App{"test data", []string{"app1", "app2", "app3"}}}
i.e. Remove the array of App, but when I try it I get an error.
Here is the working version: https://play.golang.org/p/2THGtDvlu01
I tried to change the template to
{{ range . -}}
{range $i,$a := .Command}{{if gt $i 0 }} && {{end}}{{.}}{{end}}
{{end}}
But I got an error of type mismatched, any idea how to fix it?
package main
import (
"log"
"os"
"text/template"
)
func main() {
// Define a template.
const tmpl = `
echo &1
{{range $i,$a := .Command}}{{if gt $i 0 }} && {{end}}{{.}}{{end}}
echo 2
`
// Prepare some data
type App struct {
Data string
Command []string
}
data := App{"test data", []string{"app1", "app2", "app3"}}
// Create a new template and parse into it.
t := template.Must(template.New("tmpl").Parse(tmpl))
// Execute the template with data
err := t.Execute(os.Stdout, data)
if err != nil {
log.Println("executing template:", err)
}
}
Playground example
Gives the output
echo &1
app1 && app2 && app3
echo 2
Program exited.
If you remove the []App from your code, you also need to remove the range used in the template.

Include multiple patterns in regex word break

I have the following program which uses regex to search for a pattern and replaces it a key word.
Sample as shown below will replace names like "Incorp","Inc.","Inc corp" with "Inc".
package main
import (
"fmt"
"regexp"
)
func replaceWholeWord(input string, patterns map[string]string) string {
for searchPattern, replacePattern := range patterns {
re, _ := regexp.Compile(`(?i)(^|\s)` + regexp.QuoteMeta(searchPattern) + `(\s|$)`)
input = re.ReplaceAllString(input, "${1}"+replacePattern+"${2}")
}
return input
}
func main() {
patterns := map[string]string{"Inc.": "Inc", "Incorp.": "Inc", "Incorporation": "Inc", ", Incorpa.": "Inc"}
fmt.Println(replaceWholeWord("ABC Inc.", patterns))
fmt.Println(replaceWholeWord("ABC Incorp.", patterns))
fmt.Println(replaceWholeWord("ABC InCorp.", patterns))
fmt.Println(replaceWholeWord("ABC InCorporation", patterns))
fmt.Println(replaceWholeWord("ABC , InCorpa.", patterns))
}
As you can see this performance intensive as the number of patterns increase. I want to build regular expression only once and do the search and replace operation. I am facing tough time to add the those multiple
patterns in a single regex without breaking the functionality.
Edit:
I modified my program to avoid building the regexes only if the word has the pattern, this way I have avoided to the performance hit.
Please feel free to close the question.
I am not a GO developer, but a single Regular Expression pattern for what you have shown would be:
(In(c|C)(\.|orp(\.|a\.|oration)))$
UPDATE: Found the GO way.
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?i)^(.*)(?:Inc(?:\.|orp(?:\.|a|oration)??\.))(.*)$`)
fmt.Println(re.ReplaceAllString("ABC Inc.", "${1}Inc${2}"))
fmt.Println(re.ReplaceAllString("ABC Incorp.", "${1}Inc${2}"))
fmt.Println(re.ReplaceAllString("ABC InCorporation.", "${1}Inc${2}"))
fmt.Println(re.ReplaceAllString("ABC InCorpa.", "${1}Inc${2}"))
}
ABC Inc
ABC Inc
ABC Inc
ABC Inc
Why not use an "or":
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?i)^(.*)(?:Inc\.|Incorp\.|Incorporation\.|Incorpa\.)(.*)$`)
fmt.Println(re.ReplaceAllString("ABC Inc.", "${1}Inc${2}"))
fmt.Println(re.ReplaceAllString("ABC Incorp.", "${1}Inc${2}"))
fmt.Println(re.ReplaceAllString("ABC InCorporation.", "${1}Inc${2}"))
fmt.Println(re.ReplaceAllString("ABC InCorpa.", "${1}Inc${2}"))
}
See Playground:
ABC Inc
ABC Inc
ABC Inc
ABC Inc
If all you 'search & replace' are done on whole words, you can simply turn your string into a slice of words and construct a new string which replaces each word present in your map with its counterpart:
var buffer bytes.Buffer
for _, word := range words {
if val, ok := patterns[word]; ok {
word = val
}
buffer.WriteString(word)
buffer.WriteString(" ")
}

Remove quotes between letters

In golang, how can I remove quotes between two letters, like that:
import (
"testing"
)
func TestRemoveQuotes(t *testing.T) {
var a = "bus\"zipcode"
var mockResult = "bus zipcode"
a = RemoveQuotes(a)
if a != mockResult {
t.Error("Error or TestRemoveQuotes: ", a)
}
}
Function:
import (
"fmt"
"strings"
)
func RemoveQuotes(s string) string {
s = strings.Replace(s, "\"", "", -1) //here I removed all quotes. I'd like to remove only quotes between letters
fmt.Println(s)
return s
}
For example:
"bus"zipcode" = "bus zipcode"
You may use a simple \b"\b regex that matches a double quote only when preceded and followed with word boundaries:
package main
import (
"fmt"
"regexp"
)
func main() {
var a = "\"test1\",\"test2\",\"tes\"t3\""
fmt.Println(RemoveQuotes(a))
}
func RemoveQuotes(s string) string {
re := regexp.MustCompile(`\b"\b`)
return re.ReplaceAllString(s, "")
}
See the Go demo printing "test1","test2","test3".
Also, see the online regex demo.
I am not sure about what you need when you commented I want to only quote inside test3.
This code is removing the quotes from the inside, as you did, but it is adding the quotes with fmt.Sprintf()
package main
import (
"fmt"
"strings"
)
func main() {
var a = "\"test1\",\"test2\",\"tes\"t3\""
fmt.Println(RemoveQuotes(a))
}
func RemoveQuotes(s string) string {
s = strings.Replace(s, "\"", "", -1) //here I removed all quotes. I'd like to remove only quotes between letters
return fmt.Sprintf(`"%s"`, s)
}
https://play.golang.org/p/dKB9DwYXZp
In your example you define a string variable so the outer quotes are not part of the actual string. If you would do fmt.Println("bus\"zipcode") the output on the screen would be bus"zipcode. If your goal is to replace quotes in a string with a space then you need to replace the quote not with an empty string as you do, but rather with a space - s = strings.Replace(s, "\"", " ", -1). Though if you want to remove the quotes entirely you can do something like this:
package main
import (
"fmt"
"strings"
)
func RemoveQuotes(s string) string {
result := ""
arr := strings.Split(s, ",")
for i:=0;i<len(arr);i++ {
sub := strings.Replace(arr[i], "\"", "", -1)
result = fmt.Sprintf("%s,\"%s\"", result, sub)
}
return result[1:]
}
func main() {
a:= "\"test1\",\"test2\",\"tes\"t3\""
fmt.Println(RemoveQuotes(a))
}
Note however that this is not very efficient, but I assume it's more about learning how to do it in this case.

How to concatenate Service metadata for consul-template with commas

Does anyone know how to concatenate strings from consul for consul-template?
If I have a service 'foo' registered in Consul
{
"Node": "node1",
"Address": "192.168.0.1",
"Port": 3333
},
{
"Node": "node2",
"Address": "192.168.0.2",
"Port": 4444
}
I would like consul-template to generate the following line:
servers=192.168.0.1:3333,192.168.0.2:4444/bogus
The following attempt does not work since it leaves a trailing comma ,
servers={{range service "foo"}}{{.Address}}{{.Port}},{{end}}/bogus
# renders
servers=192.168.0.1:3333,192.168.0.2:4444,/bogus
# What I actually want
servers=192.168.0.1:3333,192.168.0.2:4444/bogus
I know consul-template uses golang template syntax, but I simply cannot figure out the syntax to get this working. Its likely that I should use consul-template's join but how do I pass both .Address and .Port to join? This is just a trivial example, and I'm not using indexes intentionally since the number of services could be more than two. Any ideas?
This should work.
{{$foo_srv := service "foo"}}
{{if $foo_srv}}
{{$last := len $foo_srv | subtract 1}}
servers=
{{- range $i := loop $last}}
{{- with index $foo_srv $i}}{{.Address}}{{.Port}},{{end}}
{{- end}}
{{- with index $foo_srv last}}{{.Address}}{{.Port}}{{end}}/bogus
{{end}}
I was thinking if "join" can be used.
Note "{{-" means removing leading white spaces (such ' ', \t, \n).
You can use a custom plugin.
servers={{service "foo" | toJSON | plugin "path/to/plugin"}}
The plugin code:
package main
import (
"encoding/json"
"fmt"
"os"
)
type InputEntry struct {
Node string
Address string
Port int
}
func main() {
arg := []byte(os.Args[1])
var input []InputEntry
if err := json.Unmarshal(arg, &input); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("err: %s", err))
os.Exit(1)
}
var output string
for i, entry := range input {
output += fmt.Sprintf("%v:%v", entry.Address, entry.Port)
if i != len(input)-1 {
output += ","
}
}
fmt.Fprintln(os.Stdout, string(output))
os.Exit(0)
}