Function 是否可以在PowerShell函数中自动传递开关参数?

Function 是否可以在PowerShell函数中自动传递开关参数?,function,powershell,parameters,switch-statement,Function,Powershell,Parameters,Switch Statement,我从中获得了一些灵感,创建了一个函数 到目前为止,它的工作情况与预期一样,但只有在调用函数时提供了选项开关时才起作用。 我想做的就是在不提供开关的情况下输入“getexternalip”。然后,该功能应自动使用-All开关。我也不知道为什么所有的开关都能工作 我试图设置参数$All=$true,但它不起作用,VSCode告诉我无论如何都不推荐使用它 在没有任何参数的情况下调用函数时,是否有方法自动传递-All选项开关? 如果指定了-all开关,有人能解释为什么函数返回所有信息吗 谢谢 这是我的密

我从中获得了一些灵感,创建了一个函数

到目前为止,它的工作情况与预期一样,但只有在调用函数时提供了选项开关时才起作用。 我想做的就是在不提供开关的情况下输入“getexternalip”。然后,该功能应自动使用-All开关。我也不知道为什么所有的开关都能工作

我试图设置参数$All=$true,但它不起作用,VSCode告诉我无论如何都不推荐使用它

在没有任何参数的情况下调用函数时,是否有方法自动传递-All选项开关? 如果指定了-all开关,有人能解释为什么函数返回所有信息吗

谢谢

这是我的密码:

function Get-ExternalIP {
  [CmdletBinding()]
  param (
    [parameter(Mandatory = $false)][switch]$Ip,
    [parameter(Mandatory = $false)][switch]$HostName,
    [parameter(Mandatory = $false)][switch]$City,
    [parameter(Mandatory = $false)][switch]$Region,
    [parameter(Mandatory = $false)][switch]$Country,
    [parameter(Mandatory = $false)][switch]$Location,
    [parameter(Mandatory = $false)][switch]$Provider,
    [parameter(Mandatory = $false)][switch]$PostalCode,
    [parameter(Mandatory = $false)][switch]$TimeZone,
    [parameter(Mandatory = $false)][switch]$All
  )

  $IpInfo = Invoke-RestMethod https://ipinfo.io/json

  switch ($PSBoundParameters.Values) {
    $Ip { $IpInfo.ip }
    $HostName { $IpInfo.hostname }
    $City { $IpInfo.city }
    $Region { $IpInfo.region }
    $Country { $IpInfo.country }
    $Location { $IpInfo.loc }
    $Provider { $IpInfo.org }
    $PostalCode { $IpInfo.postal }
    $TimeZone { $IpInfo.timezone }
    Default { $IpInfo }

  }

}

Get-ExternalIP

如果您打印出$psboundparameters.values是什么,当您不使用“-all”时,您将看到它是空的,否则它有一个值$true

您可以使用参数集默认设置为
All
选项:

[CmdletBinding(DefaultParameterSetName='Everything')]
param(
    [Parameter(ParameterSetName='Something')][switch]$Ip,
    [Parameter(ParameterSetName='Something')][switch]$HostName,
    [Parameter(ParameterSetName='Something')][switch]$City,
    [Parameter(ParameterSetName='Something')][switch]$Region,
    [Parameter(ParameterSetName='Something')][switch]$Country,
    [Parameter(ParameterSetName='Something')][switch]$Location,
    [Parameter(ParameterSetName='Something')][switch]$Provider,
    [Parameter(ParameterSetName='Something')][switch]$PostalCode,
    [Parameter(ParameterSetName='Something')][switch]$TimeZone,
    [Parameter(ParameterSetName='Everything')][switch]$All
)

# set up dictionary to hold the switch-name-to-ipinfo-names
$Options = [Ordered]@{
  'Ip' = 'ip'
  'HostName' = 'hostname'
  'City' = 'city'
  'Region' = 'region'
  'Country' = 'country'
  'Location' = 'loc'
  'Provider' = 'org'
  'PostalCode' = 'postal'
  'TimeZone' = 'timezone'
}

$IpInfo = Invoke-RestMethod https://ipinfo.io/json

# Select all options
$selected = $Options.Keys
if($PSCmdlet.ParameterSetName -ne 'Everything'){
    # 1 or more switches were passed, select only those options
    $selected = $selected.Where({$PSBoundParameters[$_]})
}

# Create a new object with the selected options as properties
$properties = [ordered]@{}
foreach($prop in $selected){
  $properties[$prop] = $IpInfo.($Options[$prop])
}

# return the new object
return [pscustomobject]$properties

通过使用参数集名称来确定是否输出所有内容(然后返回),无论用户是传递
-All
,还是根本不传递开关,行为都是相同的。

我将使用
$PSBoundParameters。键作为开关正确命名,以匹配返回的信息:

function Get-ExternalIP {
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$false)][switch]$Ip,
        [parameter(Mandatory=$false)][switch]$HostName,
        [parameter(Mandatory=$false)][switch]$City,
        [parameter(Mandatory=$false)][switch]$Region,
        [parameter(Mandatory=$false)][switch]$Country,
        [parameter(Mandatory=$false)][switch]$Location,
        [parameter(Mandatory=$false)][switch]$Provider,
        [parameter(Mandatory=$false)][switch]$PostalCode,
        [parameter(Mandatory=$false)][switch]$TimeZone,
        [parameter(Mandatory=$false)][switch]$All
    )
    $IpInfo = Invoke-RestMethod https://ipinfo.io/json
    if ($All) { return $IpInfo }

    # exclude Common Parameters
    $commonParams = 'Debug','ErrorAction','ErrorVariable','InformationAction','InformationVariable','OutVariable',
                    'OutBuffer','PipelineVariable','Verbose','WarningAction','WarningVariable','WhatIf','Confirm'

    $items = (@($PSBoundParameters.Keys) | Where-Object { $commonParams -notcontains $_ }) -replace
             'Location', 'loc' -replace 'Provider', 'org' -replace 'PostalCode', 'postal'

    $IpInfo | Select-Object $items
}

如前所述,有一种比上面代码中显示的更好的方法来检索使用过的开关

与其过滤掉已知的公共参数,不如根据$IpInfo对象中返回的属性检查$PSBoundParameter键

$items = ($PSBoundParameters.Keys -replace 'Location', 'loc' -replace 'Provider', 'org' -replace 'PostalCode', 'postal').Where({ $_ -in $IpInfo.psobject.Properties.Name })
并提供优雅的解决方案;让我补充一下你的具体问题:

如果指定了
-all
开关,有人能解释为什么函数返回所有信息吗

您的
switch
语句有一个
Default
分支,如果其他分支的条件都不满足,就会调用该分支由于
-All
开关值没有分支,因此如果
-指定了所有

相反,如果不传递任何参数,则永远不会输入
switch
语句,因为对于集合值输入,
switch
语句隐式循环该集合的元素。 如果没有传递参数,
$PSBoundParameters.Values
是一个空集合,因此没有要枚举的内容


Mathias答案中的参数集方法是这个问题的最简单解决方案,它的另一个优点是使
-All
开关和特定属性名称开关相互排斥。

做得很好;需要注意的一点是:如果传递了一个或多个开关,问题中的函数只输出值-它们没有包装在自定义对象中,这与函数所做的不同。这是不够的,因为公共参数也反映在
$PSBoundParameters
中,因此,检查
$PSBoundParameters.Count-eq 0
会给你一个错误的否定,如果说,
-Verbose
是唯一通过的开关。我没有投反对票,但你应该写一个答案的大致描述,并详细说明。@WasifHasan投反对票的是我。虽然答案显然很容易解释,但它的主要问题是我指出的概念缺陷。@mklement0哦,是的,这是一个更好的方法。我甚至没想到。关于输出。OP想要什么还不是很清楚:一个值数组还是一个具有属性的对象。在他/她的代码中,返回值是一个值数组,除非使用默认选项。那么它就是一个物体。。就个人而言,我总是希望得到对象的结果。我同意包装器可能是更好的方法(除非典型的用例是只使用一个开关),但我认为当解决方案涉及到问题中代码的行为变化时,在答案中总是值得指出,甚至-也许特别是-如果这是一个更好的改变。P.S.:我刚刚注意到您的解决方案依赖于
选择对象
的一个令人惊讶的、未记录的行为:使用一个空的属性数组来选择,它传递输入对象(如果没有传递与
-All
相反的开关,则会发生这种情况);e、 例如,
(gi/| select@()).GetType().FullName
感谢您抽出时间更改函数。就像我想的那样。我也试过Mathias R.Jessen发布的一个,它也对我有用。我将研究这两种变体,并决定哪一种最适合我的目的。谢谢。@mozzie谢谢你的反馈,谢谢你发布这个有趣的问题。谢谢你的精彩解释。现在一切都有意义了。