From 64949ea305004cb51bbabbe8418b31dd3eb95c02 Mon Sep 17 00:00:00 2001 From: David PAVLOVSCHII Date: Wed, 29 Jul 2026 19:36:37 +0300 Subject: [PATCH] util/sh: strip quotes when splitting build parameters Signed-off-by: David PAVLOVSCHII --- util/sh/sh.go | 39 ++++++++++++++++++++++++++++---- util/sh/sh_test.go | 56 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/util/sh/sh.go b/util/sh/sh.go index cb94f9ca..a39fe1de 100644 --- a/util/sh/sh.go +++ b/util/sh/sh.go @@ -18,8 +18,8 @@ import ( "fmt" "os" "os/exec" - "regexp" "strings" + "unicode" ) // Verbose enables verbose output @@ -38,8 +38,39 @@ func RunCommand(name string, arg ...string) error { return cmd.Run() } -// SplitParameters splits shell command parameters, taking quoting in account. +// SplitParameters splits shell command parameters, taking quoting into account. +// Single and double quotes group text without becoming part of the parameter. func SplitParameters(s string) []string { - r := regexp.MustCompile(`'[^']*'|[^ ]+`) - return r.FindAllString(s, -1) + var ( + params []string + current strings.Builder + inParam bool + quote rune + ) + for _, r := range s { + switch { + case quote != 0: + if r == quote { + quote = 0 + continue + } + current.WriteRune(r) + case r == '\'' || r == '"': + quote = r + inParam = true + case unicode.IsSpace(r): + if inParam { + params = append(params, current.String()) + current.Reset() + inParam = false + } + default: + current.WriteRune(r) + inParam = true + } + } + if inParam { + params = append(params, current.String()) + } + return params } diff --git a/util/sh/sh_test.go b/util/sh/sh_test.go index 5ba98da0..b3b091b8 100644 --- a/util/sh/sh_test.go +++ b/util/sh/sh_test.go @@ -14,17 +14,57 @@ package sh import ( - "strings" + "reflect" "testing" ) func TestSplitParameters(t *testing.T) { - in := `-a -tags 'netgo static_build'` - expect := []string{"-a", "-tags", `'netgo static_build'`} - got := SplitParameters(in) - for i, g := range got { - if expect[i] != g { - t.Error("expected", expect[i], "got", g, "full output: ", strings.Join(got, "#")) - } + for _, tc := range []struct { + name string + in string + want []string + }{ + { + name: "empty", + in: "", + want: nil, + }, + { + name: "blanks only", + in: " \t ", + want: nil, + }, + { + name: "unquoted", + in: "-a -tags netgo", + want: []string{"-a", "-tags", "netgo"}, + }, + { + name: "extra blanks", + in: " -a \t -mod=vendor ", + want: []string{"-a", "-mod=vendor"}, + }, + { + name: "single quotes", + in: `-a -tags 'netgo static_build'`, + want: []string{"-a", "-tags", "netgo static_build"}, + }, + { + name: "double quotes", + in: `-a -tags "netgo static_build"`, + want: []string{"-a", "-tags", "netgo static_build"}, + }, + { + name: "quotes attached to the flag", + in: `-gcflags="all=-N -l"`, + want: []string{"-gcflags=all=-N -l"}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + got := SplitParameters(tc.in) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("SplitParameters(%q) = %q, want %q", tc.in, got, tc.want) + } + }) } }