Feature Request: Support passing command line args with -Command option.
See original GitHub issueSummary 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 https://github.com/PowerShell/PowerShell/issues/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
Issue Analytics
- State:
- Created 2 years ago
- Comments:14 (7 by maintainers)
FWIW, this was first proposed as a breaking change in 2017, pre-v6, in the context of better aligning the PowerShell CLI with that of POSIX-compatible shells:
I think
-CommandWithArgs
(perhaps aliased to-ca
) would help, but to avoid confusion around the syntax for-Args
I would simply omit-Args
and use purely positional processing: the first argument following-CommandWithArgs
would be the PowerShell code, and all remaining arguments would implicitly be the arguments to pass to it.That is, the equivalent of the following
sh
command line (note the unfortunate need for-
, which binds to$0
, which is something we can avoid in PowerShell):would then be:
Note that, as in
sh
and Python, for instance, the$args | % { "[$_]" }
argument would implicitly act like a script block, so no need for{ ... }
enclosure; to put it differently: the argument would act like an anonymous, in-memory script file.FYI, for folks looking for a short term workaround, I’m using [Environment]::GetCommandLineArgs and a carefully placed line comment to work around this limitation for now. Posting here as it might help others with the same issue.
Workaround: https://github.com/aaronla-ms/powershell-tips-and-tricks/blob/main/examples/embed.cmd