Repository navigation
CLI: -File arguments don't recognize [bool] parameter values when passed as separate arguments #10838
Description
In response to #4036, passing Booleans to [switch] parameters was fixed.
However, the problem persists for [bool] parameters if you pass the value as a separate argument (-param value), which is the typical syntax; by contrast, -param:value (the form that you must use with [switch] parameters) works fine (but using : and whitespace - -param: value - is also broken).
While [bool] parameters are rare, there's no reason for them not to be supported.
Additionally, the -param: value syntax form (: separator and whitespace) also doesn't work with [switch] parameters, only -param:value does (:, but no whitespace).
Also, given that [bool] parameters (but not [switch] parameters) also accepts numbers inside PowerShell - notably 1 for $true and 0 for $false (e.g., & { param([bool] $p) $p } 1 yields $true) - passing numbers should be supported too.
Steps to reproduce
Describe "-File with Booleans" {
BeforeAll {
Push-Location testdrive:\
'param([bool] $foo) $foo' > testB.ps1
'param([switch] $foo) $foo.IsPresent' > testS.ps1
}
# [switch] parameter
It "[switch] param: `$true can be passed with ':' and *no* whitespace" {
pwsh -noprofile -file ./testS.ps1 -foo:`$true | Should -Be 'True'
}
It "[switch] param: `$true can be passed with ':' *with* whitespace" {
# Note the escaped $, so that '$true' is passed as a string.
# However, it should work even without escaping, in which case 'True' is passed.
pwsh -noprofile -file ./testS.ps1 -foo: `$true | Should -Be 'True'
}
# [bool] parameter
It "[bool] param: `$true can be passed with ':' and *no* whitespace" {
# Note the escaped $, so that '$true' is passed as a string.
# However, it should work even without escaping, in which case 'True' is passed.
pwsh -noprofile -file ./testB.ps1 -foo:`$true | Should -Be 'True'
}
It "[bool] param: `$true can be passed with ':' *with* whitespace" {
pwsh -noprofile -file ./testB.ps1 -foo: `$true | Should -Be 'True'
}
# A [bool] parameter - unlike [switch] - *requires* an explicit argument,
# so you should be able to pass it with the customary value-as-separate
# argument syntax.
It "[bool] param: `$true can be passed as a *separate argument*" {
pwsh -noprofile -file ./testB.ps1 -foo `$true | Should -Be 'True'
}
# A [bool] parameter - unlike [switch] - also supports *numbers* as arguments
# where 0 is coerced to $false and 1 (any nonzero number) to $true
It "[bool] param: 1 can be passed for $true" {
pwsh -noprofile -file ./testB.ps1 -foo:1 | Should -Be 'True'
}
It "[bool] param: 0 can be passed for $false" {
pwsh -noprofile -file ./testB.ps1 -foo:0 | Should -Be 'False'
}
AfterAll {
Pop-Location
}
}Expected behavior
The test should pass.
Actual behavior
All tests except the ones with syntax form -foo:$true fail:
Expected $true, but got @('.../test.ps1 : Cannot process argument transformation on parameter 'foo'.
Cannot convert value "System.String" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.
Environment data
PowerShell Core 7.0.0-preview.4
Activity
[-]CLI: -File arguments don't recognize [bool] parameters[/-][+]CLI: -File arguments don't recognize [bool] parameter values as separate arguments[/+][-]CLI: -File arguments don't recognize [bool] parameter values as separate arguments[/-][+]CLI: -File arguments don't recognize [bool] parameter values when passed as separate arguments[/+]iSazonov commentedon Oct 19, 2019
Should we escape $ in $true?
mklement0 commentedon Oct 19, 2019
Ilya (@iSazonov)
Good point, but it makes no difference here, because the target
pwshinstance should recognized both literal strings$trueandTrue, the latter being what is passed without escaping.For clarity I've added escaping and a comment to the OP.
Note that the tests do succeed - both with literal
$trueandTrue- if you use the-param:valuesyntax (:as separator, no whitespace):iSazonov commentedon Oct 19, 2019
It seems the same as:
mklement0 commentedon Oct 19, 2019
No, it's not the same: we're talking about the CLI here, where all arguments are of necessity initially strings, and you need some way to pass a Boolean when you use
-File.The inability to pass a Boolean with
-File- still a problem in Windows PowerShell - led to the fix in #4036, but that fix was incomplete, which is what this issue is about.iSazonov commentedon Oct 20, 2019
I see two way:
mklement0 commentedon Oct 20, 2019
We don't need new parameter-parsing functionality, we just need to amend (fix) a previous fix:
That fix was to allow literal strings
$true,true,$false,falseto be converted to[bool]when bound to[switch]or - less commonly -[bool]parameters when the CLI is used with-File.This now works in principle, except in particular syntax forms, which should obviously not be treated any different than its equivalent forms.
As stated,
-foo:$falseworks, but neither-foo $falsenor-foo: $falsedo - even though these are just syntax variations.Note that
-foo $false- whitespace-only separator - only applies to[bool]parameters, because with[switch]parameters you must use:(given that switch parameters never treat a separate argument as their value).Steve Lee (@SteveL-MSFT), you implemented the original fix in #4178 - does this make sense to you?
iSazonov commentedon Oct 20, 2019
If somebody run ps1 file in pwsh session and then do the same in command line (copy-paster) different behavior may surprise the user.
mklement0 commentedon Oct 20, 2019
It may, but it shouldn't:
If you want the same behavior as inside PowerShell, invoke the CLI with
-Commandrather than-File.It was a deliberate design decision to treat all
-Filearguments as literal strings, initially, before binding to the target script's parameters.This doesn't preclude later conversions when binding to the script's parameters, such as from string to
[int], but it does preclude passing arguments such as@{foo=1}and(1+2)and expecting them to be evaluated up front, as expressions as they would inside PowerShell.If you disagree with this design, I suggest you create a new issue.
An exception to the design was deliberately put in place to allow binding strings
$true,true,$false,falseto[switch]and[bool]parameters when binding calling via-File(something that doesn't work inside PowerShell - try& { param([bool] $p) } '$true'), as it would otherwise be fundamentally impossible to pass a Boolean via-File.The issue at hand is that this exception wasn't implemented comprehensively.
mklement0 commentedon Oct 20, 2019
However, there is one problematic aspect about how the exception was implemented, Steve Lee (@SteveL-MSFT), though it may not matter much in practice:
The Boolean-like strings are unconditionally converted to
[bool]- even if the parameter they bind to is untyped:The right behavior is to retain them as strings in that event, just like a "number string" such as
1remains a string when bound to an untyped parameter via-File.iSazonov commentedon Oct 21, 2019
Conditional conversion means a binder enhancement which is likely unjustified complexity.
SteveL-MSFT commentedon Oct 21, 2019
Michael Klement (@mklement0) seems like this is becoming a slippery slope. At some point, it seems that using
-Commandis a good enough workaround.mklement0 commentedon Oct 21, 2019
I'm personally fine with leaving the unconditional conversion mentioned in my previous comment be, but I don't think there's anything slippery about the issue as original reported:
Supporting a feature in only one syntax form, but not in others that should always result in the same behavior is a baffling inconsistency that is well worth fixing.
BrucePay commentedon Oct 21, 2019
Michael Klement (@mklement0) Passing commands to an executable - any executable - requires that the arguments be passed as literal strings because that's what the operating systems support. All type information is lost so trying to get the exact semantics of calling a PowerShell function from within PowerShell is tilting at windmills. As Steve Lee (@SteveL-MSFT) says, after a while you have to accept good enough. If there's something that just doesn't work, then it needs to get fixed (encoding scriptblocks, Steve's fix) but otherwise it just leads to playing wack-a-mole with bugs.
mklement0 commentedon Oct 21, 2019
Bruce Payette (@BrucePay): To succinctly recap my previous comment:
All that is needed is to fix the incomplete fix that was implemented in #4178, as detailed in the OP.
adaviding commentedon Oct 17, 2024
This is still an important issue. I hit it today. It still has not been addressed. I request that it be re-opened.
Using powershell 7.4.5 from bash prompt on Ubuntu 22.04: