PowerShell: Unable to provide .ps1 script multiple values for a single parameter (when called from a non-pwsh environment)
Prerequisites
- Write a descriptive title.
- Make sure you are able to repro it on the latest released version
- Search the existing issues.
- Refer to the FAQ.
- Refer to Differences between Windows PowerShell 5.1 and PowerShell.
Steps to reproduce
It’s currently impossible to supply an ‘array’ of strings as an argument. Of course pwsh should not attempt to parse any input (so ./script.ps1 -x a,b
should NOT result in $x = ('a','b')
), but there seems to be no way to do this (calling from within a pwsh shell notwithstanding, naturally).
For instance a cron job script cannot successfully call the below powershell without script-specific hacks like [Parameter(ValueFromRemainingArguments=$true)]
(which aren’t applicable to multi-parameter applications).
$ cat test.ps1
#!/bin/pwsh
Param($x)
$x[0]
'!'
$x[1]
This might be better suited as a feature request but at the very least the error message is definitely not correct. Other than this niggle pwsh does an absolutely amazing job at argument parsing.
Expected behavior
$ ./test.ps1 -x a -x b #from bash, for example
a
!
b
Actual behavior
#gives error that's not even applicable to the situation (asks to use "array syntax", an in-pwsh structure)
Error details
test.ps1: Cannot bind parameter because parameter 'x' is specified more than once. To provide multiple values to parameters that can accept multiple values, use the array syntax. For example, "-parameter value1,value2,value3".
Environment data
Name Value
---- -----
PSVersion 7.3.6
PSEdition Core
GitCommitId 7.3.6
OS Linux 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21)
Platform Unix
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Visuals
No response
About this issue
- Original URL
- State: open
- Created 10 months ago
- Comments: 28 (13 by maintainers)
No, what you’ve done is completely side-stepped the issue, and ran with the cut-down minimal reproducible example to test it. Like with Dmitry suggesting
ValueFromRemainingArguments
which I mentioned in the original post as insufficient (introduce a variable $y also expecting multiple values…), your solution ignores ‘calling from a non-pwsh env/should not attempt to parse’, changing the problem from “how do I pass in my arguments as the script expects” to “now I need to write a script to call my script”, a much more convoluted issue!Consider attempting
$x = ''''$!%','arg2'
; we now no longer just need to worry about escaping in the calling language (which we already had to and is the onus of the programmer) but ALSO escaping forpwsh
(which the caller might not even use, remember they’re calling our script, not necessarily authoring it). The-command
framework would yield…A simpler way of doing this would be to serialise your inputs (say, via json and ConvertFrom-Json in your
-command
) and maybe also encode for good measure (say via base64 in the calling language, and back before the convertfrom).As Michael realised,
-x -x
is the only backwards-compatible solution and has precedent in all other shells & multi-var-requiring posix commands. The only issue I can foresee is people relying on the error message as a sanity check. For which I could propose a flag forpwsh
;-AcceptMultipleParameters
, for which the crunchbang would become#!/bin/pwsh -AcceptMultipleParameters -File
(I think), with zero change in existing behaviour (though I still strongly believe the existing behaviour to be not-usefully limiting).He also concurred with my last thought there that this may make it a change request, but the verbiage of the error message itself is still “buggy”. With the fix to #1908 finally permitting devs to use pwsh in posix environments (and indeed allow us Windows users to drop
cmd
completely), this is a lesser issue nevertheless hindering users from taking our scripts into posix environments, and it would be positively fantastic to see a proper solution.To flesh the scenario out a bit:
A script with a shebang line runs in a child process, implicitly via
pwsh -File
; this allows such scripts to be general-purpose CLIs on Unix, directly callable from other shells, such as Bash.To PowerShell, such a script is an external program (as an explicit
pwsh -File
call would be), and the usual rules for such calls apply - which are distinct from passing arguments to in-process.ps1
script calls.Since there is no array support, a robust emulation of one in combination with a named parameter is to allow binding this parameter multiple times, which is also not supported.
In external utilities, support for passing an option multiple times isn’t unusual, e.g.
curl
’s-o
option (curl example.com example.net -o aa -o bb
)This syntax does not work & doubtful it ever will as it makes little sense in calling a parameter twice like this
./test.ps1 -x a -x b
this however does
./test.ps1 -x 'a','b'
@237dmitry: Simpler, yes - but limited to unnamed arguments. (Conceivably, you emulate named arguments by examining the positional arguments and treating those that look like parameter names as such, but that would be quite cumbersome).
It simpler to deal with
$args
:script a b
Yes, I’d say this is a feature request:
Shebang line-based invocation is the same as a CLI invocation with
pwsh -File
, which doesn’t support PowerShell-style arrays - onlypwsh -Command
calls do.PowerShell fundamentally disallows binding the same parameter multiple times (such as the two
-x
arguments in your example), which is what the error message indicates.pwsh -Command
calls.In limited scenarios, the
ValueFromRemainingArguments
workaround offered by @237dmitry may do, but, due to its ambiguity, it isn’t a robust solution.The only robust - but cumbersome - workaround I see at the moment would be to pass all arguments as a single,
,
-separated string argument (e.g.,-x a,b
, quoted as necessary), and make the target script parse it into elements with-split ','
From what I can tell, the only way to provide a proper solution that doesn’t break backward compatibility is to indeed allow binding a parameter multiple times, assuming it is either untyped (implied
[object]
) or array-typed - and this definitely calls for a feature request (and my sense is that changes to the parameter binder have to be justified by a pressing need).It works in this variant (NOT printing
x
parameter explicitly):or: