-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Fix Cmd interpolation with environments #37070
Comments
I wonder if this should still error if they set the same environment variable with different values. There's no clearly correct ordering in that case. |
We could do that, which would involved parsing the environment blocks, which I'm fine with. But I also think it should be fine to layer them from left to right, so that the last added one takes precedence over all previous one. |
Alright, I have addressed one half of this with #37244, however I was stymied in fixing the second half of this due to the following very surprising behavior:
It's doing an outer product between the values within the |
If you splice two or more arrays of strings into the same word of a command, it produces the outer product of the words like the shell does with julia> names = ["foo", "bar", "baz"]
3-element Vector{String}:
"foo"
"bar"
"baz"
julia> exts = ["txt", "md", "tex"]
3-element Vector{String}:
"txt"
"md"
"tex"
julia> `rm -f $names.$exts`
`rm -f foo.txt foo.md foo.tex bar.txt bar.md bar.tex baz.txt baz.md baz.tex` Now a julia> opts = `-r -f`
`-r -f`
julia> files = `foo bar baz`
`foo bar baz`
julia> `rm $opts $files`
`rm -r -f foo bar baz` In this situation you want to splice the commands into another command as scalars, not as arrays of words because they're going into a shell-quoted context. It seems like you should be shell escaping them in order to do that, no? julia> `sh -c "$(Base.shell_escape(cmd_foo)); $(Base.shell_escape(cmd_bar))"`
`sh -c 'echo foo; echo bar'` |
I do agree that the cartesian product behavior is overly featurey and should be removed in 2.0. |
Ah, I didn't realize the quotes had such an impact. That makes sense then, and I agree that it should be removed in 2.0, but it's easy enough to work around for now. :) |
I've given up on |
If you want to interpolate a command into a shell call like this, then you need to |
Right, that makes it difficult to do things like |
The fact that command objects interpolated into other command objects in a scalar context like this does the Cartesian word generation thing is a pretty much an accidental "feature" due to Cmd objects becoming more array-like over time. I highly doubt that anyone is relying on it, and unlike the same behavior for normal vectors of strings, it isn't documented, so we could probably change it. What you seem to want here is for interpolation of a command object into a scalar command context to automatically quote the interpolated command and keep the environment associated with it. Shell quoting is indeed one way to scalarize a command object, and it would be convenient here, but I'm a little concerned that it's a bit too magical and may not be the "one true way" to handle this situation. For example, what if you did this instead? `rm -f $cmd.txt` Would shell quoting |
I do think that's the least-surprising thing to do. I don't know any other tool that does the kind of outer product thing that our |
Shells do it: $ echo {foo,bar,baz}.{txt,tex,md}
foo.txt foo.tex foo.md bar.txt bar.tex bar.md baz.txt baz.tex baz.md |
But that's using the wildcard syntax that only does exactly that behavior; you can take arrays and insert them without this behavior as long as you don't use the
The issue is that instead of explicitly requesting combinations, it's happening any time I use a |
That's just a manifestation of the shell's weak distinction between arrays and strings with spaces. I don't think that what the shell is doing there actually makes much sense to be honest. |
I think we can make some improvements to
Cmd
and environment handling.Problems:
setenv(cmd, "FOO" => "BAR")
will unconditionally overwrite all environment settings already set withincmd
.`$foo $bar`
wherefoo
andbar
are bothcmd
objects with environment variables embedded throws an error.I propose the following:
Change
Cmd(cmd::Cmd, env=env)
andsetenv(cmd::Cmd, env)
to merge environments rather than replace them. Functionally, merging can be as simple asvcat(old_env, new_env)
, since the OS will replace overlapping environments properly. This would act a little strangely with hashing, though, ashash(c1.env)
!=hash(c2.env)
even though dumping the environment of the eventual process would show the same environment. This might be a feature though, as perhaps there are OS bugs where the environment block is not properly parsed or something. We probably shouldn't pretend that acmd.env
is a dictionary when it really isn't, despite most users using it like one.Change
cmd_gen()
(theCmd
interpolation argument processor) to no longer special-case the firstCmd
object it finds, and to instead concatenate environment blocks from allCmd
objects. This would allow you to`$foo $bar`
with two different sets of environment variables set. Of course, iffoo
setsPATH
to something, andbar
sets it to something else, it would not intelligently combine the two, you'd just getbar
'sPATH
, but that is only to be expected.These changes will allow us to stop using
withenv()
in JLL packages, which is thread-unsafe. Right now, theENV
block is the only realistic method to cooperatively layer environments, as it allows a natural way to set default environment values that can be overridden by the user. However, due to its thread-unsafety, it's ultimately undesirable. It would be much better to change the current API from:to something more like:
where the
git()
function would return aCmd
object with all necessary environs set within it. Without solving this issue, the second syntax will result in an error, asgit
will be unable to find its dependent libraries.The text was updated successfully, but these errors were encountered: