英文:
How to set mandatory attribute of a parameter to $true conditionally in PowerShell?
问题
以下是您要翻译的内容:
"属性mandatory接受ScriptBlocks,但经过我的测试以及在其他答案中看到的情况,它总是返回$true
。
function Test-Function {
param (
[Parameter (Position = 0)]
[string]$First,
[Parameter (Position = 1, Mandatory = ({
return $false
}))]
[string]$Second
)
Write-Host 'First: $First'
Write-Host 'Second: $Second'
}
Test-Function -First 'boo'
我不想使用动态参数,因为Get-Help
不显示它们(除非我漏掉了什么?),而且我已经在多个地方使用ParameterSets,所以不能破坏它们的逻辑。
有什么其他方法可以实现我想要的?
我的目标是首先从用户配置文件中读取数据,如果参数的值已经在该文件中可用,那么该参数应该从强制变为可选。"
英文:
The mandatory attribute accepts ScriptBlocks but as I've tested it and seen in other answers, it's always returning $true
function Test-Function {
param (
[Parameter (Position = 0)]
[string]$First,
[Parameter (Position = 1, Mandatory = ({
return $false
}))]
[string]$Second
)
Write-Host "First: $First"
Write-Host "Second: $Second"
}
Test-Function -First "boo"
I don't want to use Dynamic parameters because Get-Help
doesn't show them (unless I'm missing something?), and I'm already using ParameterSets a lot, so can't break their logic.
What are some other ways I can achieve what I want?
my goal is to first read from a user configuration file and if a value for a parameter is already available in that file then the parameter should turn from mandatory to optional.
答案1
得分: 1
以下是您要翻译的内容:
The
Mandatory
attribute accepts ScriptBlocksIt technically accepts a script block, but passing one is pointless, given that the property's type is
[bool]
:[bool] { <# whatever #> }
is always$true
, as with any non-primitive object cast to[bool]
.<sup>[1]</sup>Only attributes explicitly designed to accept script blocks support them meaningfully, typically via the attribute constructor rather than (only) via one of its properties (e.g.
[ValidateScript({ <# whatever #> })]
)
我已经完成对上述内容的翻译。如果您需要更多翻译或其他帮助,请告诉我。
英文:
<!-- language-all: sh -->
Note:
-
The next two sections make general observations.
-
The bottom section shows an alternative solution to your own.
> The Mandatory
attribute accepts ScriptBlocks
It technically accepts a script block, but passing one is pointless, given that the property's type is [bool]
: [bool] { <# whatever #> }
is always $true
, as with any non-primitive object cast to [bool]
.<sup>[1]</sup>
Only attributes explicitly designed to accept script blocks support them meaningfully, typically via the attribute constructor rather than (only) via one of its properties (e.g. [ValidateScript({ <# whatever #> })]
)
> I don't want to use dynamic parameters because Get-Help
doesn't show them
Get-Help
(and the -?
parameter) do show them, but only if the runtime conditions for their addition are met.
Therefore, the only way to always make a dynamic implementation of your -Second
parameter show would be to add it unconditionally inside the DynamicParam
block used to implement dynamic parameters, and to only make its Mandatory
property dynamic, depending on runtime conditions.
However:
-
Dynamic parameters are generally nontrivial to implement.
-
Assigning a default value to a dynamic parameter - which is what you want - appears to be unsupported (assigning to the
.Value
property of a dynamic parameter is in effect quietly ignored).
The following alternative to your solution moves all processing into the $(...)
subexpression:
function Test-Function {
param (
[Parameter(Position = 0)]
[string] $First
,
[Parameter(Position = 1)]
[ValidatePattern('^[a-zA-Z0-9 ]+$')]
[string] $Second = $(
# Try to obtain a default value and make sure one is available.
$val = (Get-Content -Path "$env:USERPROFILE\.WDACConfig\UserConfigurations.json" | ConvertFrom-Json).policyname
if ($null -eq $val) {
throw "A -Second argument is required or must be preconfigured."
}
# A default value is available:
# Validate it based on the [ValidatePattern()] attribute.
$validatePatternAttrib = $MyInvocation.MyCommand.Parameters['Second'].Attributes.Where({ $_ -is [System.Management.Automation.ValidatePatternAttribute]})
if (-not [regex]::Match($val, $validatePatternAttrib.RegexPattern, $validatePatternAttrib.Options).Success) {
throw ("The argument `"$val`" does not match the `"$($validatePatternAttrib.RegexPattern)`" pattern.", ($validatePatternAttrib.ErrorMessage -f $val))[[bool] $validatePatternAttrib.ErrorMessage]
}
$val # Output the default value to assign it to the parameter var.
)
)
process {
Write-Host "First: $First"
Write-Host "Second: $Second"
}
}
Test-Function -First "boo"
Note:
-
While scoping the solution to the default-value code inside
$(...)
is desirable, there is added complexity due to having to dynamically apply the[ValidatePattern()]
attribute to the default value (the code could be simplified; as shown it fully emulates what the[ValidatePattern()]
does). -
Arguably, this added complexity shouldn't be necessary, as even a default value should automatically be subject to the same validation as an explicitly passed one. GitHub issue #8795 proposes just that.
<sup>[1] PowerShell allows implicit conversion to [bool]
from any type. See the bottom section of this answer for a summary of the rules.</sup>
答案2
得分: 0
So, I came up with this solution and seems to be working, haven't validated it with every possible scenario I have in mind but for now seems to be working, will update if I find any shortcoming.
function Test-Function {
param (
[string]$First,
[string]$Second = $(
((Get-Content -Path "$env:USERPROFILE\.WDACConfig\UserConfigurations.json" | ConvertFrom-Json).policyname)
)
)
begin {
if (!$Second) {
throw "Variable Second was empty"
}
}
process {
Write-Host "First: $First"
Write-Host "Second: $Second"
}
}
Test-Function -First "boo"
Caveat 1: ValidatePattern
attribute doesn't work with this method and fails to validate the default values assigned to the parameter from the file, but this will take care of it.
$UserConfig = (Get-Content -Path "$env:USERPROFILE\.WDACConfig\UserConfigurations.json" | ConvertFrom-Json)
function Test-Function {
param (
[parameter(ParameterSetName = "Installed AppXPackages", ValueFromPipelineByPropertyName = $true)]
[string]$First,
[parameter(ParameterSetName = "Installed AppXPackages", ValueFromPipelineByPropertyName = $true)]
[string]$Second = $(
($UserConfig.policyname)
)
)
begin {
if (!$Second) {
throw "Variable Second was empty"
}
elseif ($Second -notmatch '^[a-zA-Z0-9 ]+$') {
throw "The Supplemental Policy Name can only contain alphanumeric characters and spaces."
}
}
process {
Write-Host "First: $First"
Write-Host "Second: $Second"
}
}
Test-Function -First "boo" -Second "fdsf"
英文:
So, I came up with this solution and seems to be working, haven't validated it with every possible scenario I have in mind but for now seems to be working, will update if I find any shortcoming.
function Test-Function {
param (
[string]$First,
[string]$Second = $(
((Get-Content -Path "$env:USERPROFILE\.WDACConfig\UserConfigurations.json" | ConvertFrom-Json).policyname)
)
)
begin {
if (!$Second) {
throw "Variable Second was empty"
}
}
process {
Write-Host "First: $First"
Write-Host "Second: $Second"
}
}
Test-Function -First "boo"
Caveat 1: ValidatePattern
attribute doesn't work with this method and fails to validate the default values assigned to the parameter from the file, but this will take care of it.
$UserConfig = (Get-Content -Path "$env:USERPROFILE\.WDACConfig\UserConfigurations.json" | ConvertFrom-Json)
function Test-Function {
param (
[parameter(ParameterSetName = "Installed AppXPackages", ValueFromPipelineByPropertyName = $true)]
[string]$First,
[parameter(ParameterSetName = "Installed AppXPackages", ValueFromPipelineByPropertyName = $true)]
[string]$Second = $(
($UserConfig.policyname)
)
)
begin {
if (!$Second) {
throw "Variable Second was empty"
}
elseif ($Second -notmatch '^[a-zA-Z0-9 ]+$') {
throw "The Supplemental Policy Name can only contain alphanumeric characters and spaces."
}
}
process {
Write-Host "First: $First"
Write-Host "Second: $Second"
}
}
Test-Function -First "boo" -Second "fdsf"
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论