-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Description
Summary of the new feature/enhancement
Requesting support for passing arguments via command line to be passed to the command the user is requesting to be executed. Compare with python:
python.exe -c "import sys; print(sys.argv[1])" "It's `your`, not `you're`"
NOTE: this request is not about invoking PowerShell from within PowerShell as a cmdlet. I'm aware that there's special processing there that translates -Command / -Args to the command line parameters -encodedCommand and -encodedArguments. This is specifically regarding creating a process where the native command line arguments passed to the process include -Command and -Args -- e.g. invoking PowerShell form some other shell, such as bash or cmd.
Proposed technical implementation details (optional)
Propose extending -Command to support -Args when passed as command line arguments to the PowerShell host. This would apply any time that:
- The argument immediately following -Command starts with '{' and ends with '}', once whitespace has been trimmed.
- The 2nd argument after -Command is the argument -Args (ordinal case insensitive)
- -Args is followed by zero or more additional command line arguments.
When this occurs, the $Args value will be bound with an array of the remaining command line arguments following the -Args argument.
Backwards compat risk: estimated to be small, but non-zero. When attempting to use -Command with the first argument being a script block scring, I found no way to pass -Args afterward and result in a correcltly parsing powershell command, let alone a useful one. I suspect it can be done, but is unlikely to be practical. If nothing else, the change is definitely smaller in impact than the breaking changes to Set-Content between pwhs 5.1 and 6.0.
Note: proposing no change to -Command or -Args parsing when invoking the Powershell function/commandlet from within powershell. That behavior will remain unchanged -- namely, when invoking PowerShell from within Powershell, the -Command and -Args arguments are replaced with -encodedCommand and -encodedArguments before the process is launched. This already has full fidelity today.
Rationale
Note that this greatly simplifies string escaping. Being able to pass arguments after the command allows the user to only have to deal with one level of string escaping -- the current shell they're interacting with.
In contrast, schemes like powershell.exe -Command "{ param($a) echo $a }" -A 'my example', the argument my example gets parsed twice -- first in your current shell (e.g. PowerShell, Bash, or Windows Command Shell), and then again by the powershell parser when parsing it as part of the -Command argument. This makes it prohibitively difficult to forward arguments between scripts, or invoke PowerShell via exec/CreateProcess from another program.
For example, passing the string "It's `your`, not `you're`" PowerShell through another script is exceedingly difficult, e.g. if embedded in the -Command argument. For instance, if you don't quote the string, PowerShell will treat the whitespace as argument separators. Also, PowerShell will parse the "`" characters as escapes, and the "'" characters as unescaped string literals. This is on top of the unescaping the outer shell has already performed on the user input -- e.g. in bash, the user probably wrapped the above in single or double quotes in order to be able to include spaces in the argument.
As discussed in issue #15410, Powershell only support passing arguments to a -command when invoked as a function from within powershell, not when invoked externally.
Examples in other script environments:
- Python (invoked from cmd)
$ c:\bin\Python26\python.exe -c "import sys; print(sys.argv[1])" "It's
your, notyou're"
It'syour, notyou're - JavaScript/node (invoked from cmd)
$ c:\bin\node.exe -e "console.log(process.argv[1])" "It's
your, notyou're"
It'syour, notyou're - sh (invoked from cmd)
$ C:\bin\busybox.exe sh -c "echo $0" "It's
your, notyou're"
It'syour, notyou're - bash (invoked from PowerShell)
bash -c 'echo $0' "It's
your, notyou're"
It'syour, notyou're