diff --git a/osutil/env.go b/osutil/env.go index d63763a0ade..51c7dc8f252 100644 --- a/osutil/env.go +++ b/osutil/env.go @@ -22,6 +22,7 @@ package osutil import ( "fmt" "os" + "regexp" "sort" "strconv" "strings" @@ -209,6 +210,38 @@ func NewExpandableEnv(pairs ...string) ExpandableEnv { return ExpandableEnv{OrderedMap: strutil.NewOrderedMap(pairs...)} } +var envExpandRegExp = regexp.MustCompile("[a-zA-Z0-9_]+:[+-]") + +// Adds support for bash conditional syntax ${VARIABLE:+XXX} and ${VARIABLE:-XXX} +func (env *Environment) expand(value string) string { + return os.Expand(value, func(varName string) string { + loc := envExpandRegExp.FindStringIndex(varName) + if loc == nil { + return (*env)[varName] + } + envVar := string(varName[loc[0]:(loc[1] - 2)]) + operation := string(varName[loc[1]-1]) + newVal := string(varName[loc[1]:]) + envVarValue := (*env)[envVar] + switch operation { + case "-": + if envVarValue == "" { + return env.expand(newVal) + } else { + return envVarValue + } + case "+": + if envVarValue == "" { + return "" + } else { + return env.expand(newVal) + } + default: // never can really happen, but the compiler complains without it + return (*env)[varName] + } + }) +} + // ExtendWithExpanded extends the environment with eenv. // // Environment is modified in place. Each variable defined by eenv is @@ -220,9 +253,7 @@ func (env *Environment) ExtendWithExpanded(eenv ExpandableEnv) { } for _, key := range eenv.Keys() { - (*env)[key] = os.Expand(eenv.Get(key), func(varName string) string { - return (*env)[varName] - }) + (*env)[key] = env.expand(eenv.Get(key)) } } diff --git a/osutil/env_test.go b/osutil/env_test.go index cb1fba5e44f..c893998e20d 100644 --- a/osutil/env_test.go +++ b/osutil/env_test.go @@ -330,3 +330,34 @@ func (s *envSuite) TestForExecEscapeUnsafeNothingToEscape(c *C) { "XDG_STUFF=xdg-stuff", }) } + +func (s *envSuite) TestExpandEnvVariable(c *C) { + env := osutil.Environment{ + "LD_LIBRARY_PATH": "/usr/lib:/usr/local/lib", + "TMPDIR": "/var/tmp", + "A": "foo", + "B": "bad-value", + "D_default": "default", + } + + env.ExtendWithExpanded(osutil.NewExpandableEnv( + "LD_LIBRARY_PATH", "${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}/usr/lib/x86_64-linux-gnu", + "PATH", "${PATH:+$PATH:}/usr/local/bin", + "TMPDIR", "${TMPDIR:-this-wont-be}", + "NOEXISTS", "${NOEXISTS:-a-new-value}", + "A", "${A:+}", + "B", "${B:+goodvalue}", + "C", "${C:-}", + "D", "${D:-$D_default}", + )) + c.Check(env, DeepEquals, osutil.Environment{ + "LD_LIBRARY_PATH": "/usr/lib:/usr/local/lib:/usr/lib/x86_64-linux-gnu", + "NOEXISTS": "a-new-value", + "TMPDIR": "/var/tmp", + "PATH": "/usr/local/bin", + "A": "", + "B": "goodvalue", + "C": "", + "D": "default", + "D_default": "default"}) +}