-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
os/exec: add Cmd.SetEnv method #12868
Comments
I'm not actually dead set on the exact signature. Maybe taking We would also need to define whether the slice is mutated in-place or replaced. And the semantics when |
SGTM. In cmd/go/go_test.go I also found it useful to have an Unsetenv. |
I'd be happy with
Makes sense to me. |
I started to implement
So then I was back to thinking about just Maybe we shouldn't care. But if we did care, I was toying with ideas like: // EnvMod represents a modification to environment variables.
type EnvMod struct {
key string
val string
set bool
}
func Unsetenv(key string) EnvMod { return EnvMod{key: key} }
func Setenv(key, value string) EnvMod { return EnvMod{key: key, val: value, set: true} }
func (c *Cmd) ModifyEnv(mods ...EnvMod) { ... } But call sites are then kinda ugly: c.ModifyEnv(exec.Setenv("GOROOT", v), exec.Unsetenv("GOBIN")) (and calling So then I thought: maybe we just keep the functions simple: func(c *Cmd) Setenv(key, value string) {...}
func(c *Cmd) Unsetenv(key string) {...} ... but for people who care about the quadratic behavior, they can optionally use: // ModifyEnv runs fn, which should contain a series of Setenv and/or Unsetenv calls.
// Use of ModifyEnv is optional but avoids quadratic behavior eliminating duplicates
// when modifying many environment variables.
func (c *Cmd) ModifyEnv(fn func()) {
// build, cache data structure on c
fn()
// clear cache data structure on c
} There's not much (any?) precedent for such a function, though. The closest is App Engine's RunInTransaction perhaps: https://godoc.org/google.golang.org/appengine/datastore#RunInTransaction Maybe we can introduce ModifyEnv later if needed, and just accept the poor behavior of |
Perhaps we could have Setenv just append to the Env slice with custom logic: if the append would allocate, then do a pruning first (later values take precedence). Start (and Run) would also need to do a pruning as well. |
We can't dedup in Start or Run. That would be a change of behavior and it's valid (even if weird) to have multi-valued keys. And if we did dedup: first or last? Different programs pick a different one. |
Why don't we just let people who really care about the quadratic behavior futz with cmd.Env manually? |
Although I also like the variadic approach panicking on To unset you could |
UNSETKEY hack is gross. Then it's a magic environment variable you can't set. |
No no I meant that |
Oh. I also considered that but there's a difference between empty and unset and we wouldn't want to differ from os.Setenv semantics. That distinction and complaints is why we finally added os.Unsetenv. |
Since it doesn't look like it's been considered: don't forget about the approach taken by https://golang.org/pkg/strings/#NewReplacer |
@masiulaniec, good point. That's worth consideration. It doesn't quite work for the I'm increasingly thinking we should just call it |
I'm glad this task is being addressed and made easier, because I too had to write little internal helpers to help manage env vars for // environ is a slice of strings representing the environment, in the form "key=value".
type environ []string
// Unset a single environment variable.
func (e *environ) Unset(key string) {
for i := range *e {
if strings.HasPrefix((*e)[i], key+"=") {
(*e)[i] = (*e)[len(*e)-1]
*e = (*e)[:len(*e)-1]
break
}
}
} And usage is: env := environ(os.Environ())
env.Unset("FOO")
cmd.Env = env |
SGTM |
A few points.
|
I like the idea of exec.Env, and I think it's
more generally useful, too.
|
I would still like to do this. Ideally in Go 1.8. |
Accepted pending final details about the API. |
Slightly OT but as a history lesson, is this a slice to match |
@cbednarski The environment is represented as a slice mainly because in C it is a char**, both in how it is passed to the program (as the third argument to All Unix systems do support duplicate environment variables, though their effect on specific programs depends on how those programs handle the environment array. |
@ianlancetaylor Great explanation, thanks very much! |
Not sure whether this is still about Cmd.SetEnv or about the more general exec.Env I suggested. Either way, looks like it will wait for Go 1.9. |
How about this instead:
/cc @josharian |
To be clear, that means all code like this is now correct and does what people expect: cmd := exec.Command("prog")
cmd.Env = append(os.Environ(), "FOO=bar") ... without worrying about whether |
OK. |
And note that that matches bash's behavior: $ A=1 A=2 env | head -n 1
A=2 |
CL https://golang.org/cl/37586 mentions this issue. |
This is now handled by os/exec. Updates #12868 Change-Id: Ic21a6ff76a9b9517437ff1acf3a9195f9604bb45 Reviewed-on: https://go-review.googlesource.com/37698 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
@bradfitz I know this is an old issue but I wanted to ask if you can pull back environmental variables set by an executing process? I've been testing this and while I see them reflected in the executing process - I only get the original set from the Golang code. |
@alexellis This issue is closed, and in any case we don't use the issue tracker for questions. Please see https://golang.org/wiki/Questions. Thanks. (I'm not sure what you are asking, but to modify the current process environment, use |
No that wasn't the question - it seemed like this is where the design decision was made - so was hoping for a quick answer without having to sign up to anything.
Is this even possible? |
Please use a forum. You don't have to sign up. Thanks. |
I'd like to add a method to
os/exec.Cmd
to ease setting environment variables on commands before they're run.Manipulating
Env []string
lists is tedious and error-prone, especially considering potential duplicates. (since programs would others choose either the first or last occurrence, unpredictably)The proposed
Cmd.SetEnv(key, value string)
method would be documented as removing duplicates.On Windows, environment variables are case-insensitive, so the method on Windows should de-dup case insensitively.
This method would replace a handful of copies of
func mergeEnvLists
that exist in various forms around the standard library, tests, and x/* repos./cc @ianlancetaylor @adg @rsc @griesemer @robpike
The text was updated successfully, but these errors were encountered: