From 7188e1d4105c29fc5ed29dc01c6e6833653f1f5a Mon Sep 17 00:00:00 2001 From: Victor Shih Date: Sat, 9 Jul 2016 10:37:13 -0700 Subject: [PATCH] Support backslash-escaped slashes in find-and-replace substitution. (#1699) --- yaml/expander/func.go | 26 ++++++++++++++++++-------- yaml/expander/func_test.go | 6 ++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/yaml/expander/func.go b/yaml/expander/func.go index 7399b059f..3fb503453 100644 --- a/yaml/expander/func.go +++ b/yaml/expander/func.go @@ -96,24 +96,34 @@ func substituteDefault(str, key, val string) string { return str } +// unescapeBackslash is a helper function to unescape any backslashes in str. +// Note that no actual literal conversions are done. +func unescapeBackslash(str string) string { + re := regexp.MustCompile(`\\(.)`) + + return string(re.ReplaceAll([]byte(str), []byte("$1"))) +} + // substituteReplace is a helper function that substitutes paramters using // ${parameter/old/new} notation with the parameter value. A find and replace // is performed before injecting the strings, replacing the old pattern with // the new value. func substituteReplace(str, key, val string) string { - key = fmt.Sprintf("\\${%s/(.+)/(.+)}", key) + key = fmt.Sprintf(`\${%s/((?:\\.|[^\\])+)/(.+)}`, key) reg, err := regexp.Compile(key) if err != nil { return str } - for _, match := range reg.FindAllStringSubmatch(str, -1) { - if len(match) != 3 { - continue - } - with := strings.Replace(val, match[1], match[2], -1) - str = strings.Replace(str, match[0], with, -1) + match := reg.FindStringSubmatch(str) + if match == nil { + return str } - return str + + old := unescapeBackslash(match[1]) + new := unescapeBackslash(match[2]) + + with := strings.Replace(val, old, new, -1) + return strings.Replace(str, match[0], with, -1) } // substituteLeft is a helper function that substitutes paramters using diff --git a/yaml/expander/func_test.go b/yaml/expander/func_test.go index 2a9528cf5..8618635b1 100644 --- a/yaml/expander/func_test.go +++ b/yaml/expander/func_test.go @@ -53,6 +53,12 @@ func TestSubstitution(t *testing.T) { g.Assert(substituteReplace(before, "GREETING", "HELLO")).Equal(after) }) + g.It("Should substitute parameters with replacement, containing slashes", func() { + before := `echo ${GREETING/HE\//A} MONDE` + after := "echo ALLO MONDE" + g.Assert(substituteReplace(before, "GREETING", "HE/LLO")).Equal(after) + }) + g.It("Should substitute parameters with left substr", func() { before := "echo ${FOO:4} IS COOL" after := "echo THIS IS COOL"