-
Notifications
You must be signed in to change notification settings - Fork 7.2k
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
Add support for passing arguments to Invoke-Expression
#23836
Comments
I like the idea in principle, but, in my mind, to make it feasible, more changes are required:
Therefore, to me, for an enhancement to
If all of these have to be opt-ins - lest backward compatibility be broken - I fear that the additional switches needed, let alone the need to remember to use them, makes this enhancement less appealing. That said, perhaps a single switch - say irm ... | iex -s [arg ...] |
A couple of observations..
|
It is not only a very common pattern, but also a perfectly reasonably one, assuming the context of a pre-vetted (installer) script.
You would think that that's the equivalent, but it - unfortunately - isn't:
And, yes, both
An in-process solution, such as via the enhancements to |
Tutorials often include the disclaimer that error handling is omitted for clarity, however the real world does include partial failure scenarios, and error handling is part of software engineering. Running half a downloaded script at elevated privileges is not a scenario I would like to be in. In the PowerShell world the intention was for signed scripts to ensure the integrity of the script, that can only be done by downloading the entire script first in order to validate the signature. While it may be common to pipe unseen content downloaded from the internet into a shell running as root, I would never recommend it as a practice. |
The merits of the most defensive programming techniques available vs. pragmatic, real-world solutions in relation to trusted sources strike me as an entirely separate discussion, and, as such, an unhelpful distraction from what this issue is about. |
Not when is is used as a justification for the requirement of the feature, and the existing supported PowerShell pattern of downloading the script first then running solves both the problem of script integrity and passing of arguments. Compare with docker
and dotnet
Neither suggest piping the script. |
This comment was marked as resolved.
This comment was marked as resolved.
Looking at what the definition of an expression is 7. Expressions in the PowerShell language, I don't see that expressions have param blocks. In PowerShell functions, cmdlets, scripts and script blocks all support param blocks. It looks to me that what you want to use is Invoke-Command but the reason you can't is that you can't pass in the script block as pipeline input. Sounds like what you are wanting is the equivalent of
Perhaps an argument/flag on Invoke-Command to read the ScriptBlock as ValueFromPipeline would be better, ideally treating multiple lines of text as part of a single script, and waits for the entire script to be read before invoking. |
That's #8816. :) The reason why I opened this issue is to explicitly focus on passing arguments, which I think can be tackled as a separate feature without a dependency on the other features you mention.
Yes, which is unfortunate for this use case. However, the issue can currently be solved by the author of the installer by wrapping everything in
Since the installer is typically invoked from a pre-existing PowerShell session, I'll disagree here. It's a PowerShell script, it should communicate failure using the standard PowerShell way – by throwing errors. Errors also work when the installer is invoked as In practice, I don't remember ever coming across a PowerShell installer script that would call exit, but your experience may very well be different. |
Do note that the issue with partial download does NOT apply to PowerShell. |
It's a alternative proposal to #8816, focused on Note that we'd have backward compatibility either way, given that no one wants to change the existing (default) Enhancing For that reason, focusing on adding argument support alone is not enough, in my estimation: if new functionality is added, I prefer it to be one that fully addresses the use case. You're right that the
Having a single switch that opts into script file-like behavior could provide all desired behaviors: argument support, no caller-scope pollution, no Even if we leave the # Current proposal: still requires workaround for scope pollution
iex "& { $(irm ...) } @args" [arg ...] vs. # With -AsScriptFile / -s opt-in:
irm ... | iex -s [arg ...] |
My bad, I wasn't clear. What I meant is that for an installer script, compatibility with older versions of PowerShell is typically paramount. Therefore, authors of installers will not alter their installation scripts to make use of newly added features if it would make the script incompatible with older versions of PowerShell. This leads me to believe that preventing scope pollution and supporting
Not exactly. The following installer script currently works both on PowerShell 5 and the latest version, when invoked as param($Param1 = "default", $Param2)
& {
$NewVar = "hello"
echo "Using $Param1..."
# ...
} My proposal is only to simplify the code needed to pass arguments to such script from: iex "& {$(irm ...)} arg -Param2 arg2" to: irm ... | iex arg -Param2 arg2" |
So you're saying that there are installation scripts out there that explicitly compensate for the scope-pollution issue that And your proposal is focused solely on making argument-passing to such specially-crafted scripts easier? Note that the scope-pollution issue is implicitly avoided by the current invocation pattern you cite as necessary for passing arguments, If you're proposing that the mere fact of passing arguments should implicitly transition from same-scope execution to child-scope execution: I think that amounts to ill-advised, obscure behavior, as there is then no justification for A more consistent approach would be to make |
Not sure if there are scripts doing this other than mine; my point is that if they care about scope pollution, they can already handle it today. While I do agree that it would be better to have Adding the parameter would give the authors of installer scripts three options:
My guess is that if the author doesn't care (or doesn't know) about the issue, they'll go with 1), and if they do care, they'll go with 2), since it provides the best experience for all users. There's another case where the parameter may be useful, which is when a script is designed using 1), but a savvy user wants to prevent it from polluting the parent scope. However, the user has the choice of just running
Definitely not.
The value that I see in my proposal is that it does not require cooperation from the script author. The author is free to say "The installer script takes parameters. To pass them, use the following invocation: An additional point is that the default installation one-liner will be used by many users, and if the defaults are chosen well, most of the users will never need to learn how to pass parameters. The users who are picky enough to want to change the defaults are also more likely to know about the various ways of passing arguments through |
@MatejKafka, I appreciate the explanation, but to me it comes down to this: Introducing a new feature solely for the benefit of providing syntactic sugar for a well-established, but fundamentally flawed idiom is ill-advised - even if scripts that compensate for the flaws exist. The scope-pollution issue cannot be solved without an opt-in, if backward-compatibility is to be preserved. The alternative to 3. is:
|
@rhubarb-geek-nz This issue is about extending |
Invoke-Expression treats each record in the input pipeline as its own string expression
If an argument list was provided it would only make sense for a few scenarios for the same argument list to be given to multiple different expressions. If you were providing a single argument list as described by the given scenarios then it would be reasonable to expect the entire input to be treated as a single script, so all lines would be joined with newline separators before converting to a single script. If all unbound arguments were converted into an argument list it would change the behaviour for errors where the script has mistakenly missed the quotations.
|
All these behaviors (I originally didn't consider the multi-string pipeline-input case, because it doesn't apply to If and only if
In the use case with pass-through arguments, the This solution is fully backward-compatible and doesn't even change error messages for the broken invocations you mention. |
Assuming discussions about the difference between could and should with comparisons to UNIX shell are permitted. I see Invoke-Expression as being a very light-weight construct similar to POSIX shell "eval". All eval does is invoke an expression within the current process. I see very close parallels with Invoke-Expression running in the current scope and executing the given expression provided as a string. To be honest, I am very surprised the original PowerShell did not create an alias for eval resolving to Invoke-Expression. The equivalent wanted for Based on the principle of "do one thing and do it well", I think Invoke-Expression already does that. I think the proposal is to make it do something else as well. So on the topic of extending Invoke-Expression,, while it could be done, I don't think it should. |
This proposal, applied to
Note that this implies that That said, the |
Summary of the new feature / enhancement
This is a follow-up on one specific aspect of #8816.
The
irm ... | iex
pattern is well-known and widely used for installing software, similarly tocurl ... | sh
on POSIX-like systems. We may not be happy about it, but it is widely used, and projects will use continue to use it in the future unlessInvoke-Expression
is removed alltogether.#8816 floated some alternative options, converging on extending
Invoke-Command
. However, compatibility across all commonly used versions of PowerShell is vital for an installer script; a script that only works on some versions of PowerShell is not very useful as a one-liner, since now you have to give multiple one-liners and make the user choose based on the version of PowerShell they're using. This gives me a strong reason to believe that software vendors (including me) will NOT switch to any new backwards-imcompatible option to run downloaded scripts in one command.One of the limitations of
irm ... | iex
mentioned in #8816 was that there's no intuitive way to pass optional arguments to the installer script. Unlike some of the issues, I believe that this issue may be resolved in a backwards-compatible way by allowingInvoke-Expression
to receive additional arguments and passing them to the invoked script.Users on older versions of PowerShell will still be able to use the
iex "& {$(irm ...)} arg"
hack shown in the original issue, while users on new versions of PowerShell can pass arguments the way they would intuitively expect, the same way as withcurl ... | sh
.Proposed technical implementation details (optional)
Add a new
-ArgumentList
parameter toInvoke-Expression
, withParameter(ValueFromRemainingArguments = true)
. As a result, users should be able to do the following:If the script provides a
param()
block, the arguments should be bound as if the script was invoked as a scriptblock with&
.The text was updated successfully, but these errors were encountered: