如何在PowerShell中使用多个参数强制执行高级函数的位置0

如何在PowerShell中使用多个参数强制执行高级函数的位置0,powershell,Powershell,我正在将我的一个标准函数转换为高级函数。该函数基本上与我公司的服务器进行对话,并返回我需要的有关我们所拥有的产品的信息。目前,它被编码为只扫描所有服务器,但我想添加一些开关,其中MyFunc-all将扫描所有服务器,MyFunc-Single SERVERNAME将只扫描该服务器,MyFunc-Help将显示有关该函数的信息 我有工作的功能,如果你把一切都像你应该它的工作很好。我遇到的问题是,在运行函数时,强制某人在位置0处输入开关。就像我在没有开关的情况下调用函数一样,它要求为“All”提供一

我正在将我的一个标准函数转换为高级函数。该函数基本上与我公司的服务器进行对话,并返回我需要的有关我们所拥有的产品的信息。目前,它被编码为只扫描所有服务器,但我想添加一些开关,其中MyFunc-all将扫描所有服务器,MyFunc-Single SERVERNAME将只扫描该服务器,MyFunc-Help将显示有关该函数的信息

我有工作的功能,如果你把一切都像你应该它的工作很好。我遇到的问题是,在运行函数时,强制某人在位置0处输入开关。就像我在没有开关的情况下调用函数一样,它要求为“All”提供一个值。如果我将值留空或输入任何内容,我会收到一条错误消息“无法处理参数'All'上的参数转换”

这是我第一次尝试高级功能,所以我确信我遗漏了一些东西,但我认为有一种方法需要位置0输入

我正在寻找的示例(如果可能)

PS>MyFunc

PS>“由于未提供交换机,我们被要求提供一个交换机”

PS>-All或-Help或-Single SERVERNAME

PS>打印结果

function MyFunc
{
   [CmdletBinding()]
   PARAM(
      [Parameter(Mandatory=$true, Position=0)]
      [switch]$All,
      [switch]$Help,
      [switch]$Single,
      [Parameter(ValueFromRemainingArguments=$true)]
      [string]$ServerName
   )

   If($all)
   {
      $Servers = @("Server1",
             "Server2",
             "Server3",
             "Server4",
             "Server5")
              #Check servers
   }
   elseif($Single)
   {
      $Servers = @($ServerName)
              #Check server
   }
   elseif($Help)
   {
      #Print help message
   }
}

我建议对调用函数和定义其参数的方式进行一些更改

首先,去掉
-Help
开关。PowerShell具有(非常好的)帮助语义,您应该使用这些语义。为函数定义,然后用户可以调用
Get Help MyFunc
Help MyFunc
查看函数的所有信息。最好的部分是,如果您只需要参数以及需要哪些参数,那么您不必做任何事情;它会自动为您生成(现在就开始尝试获取函数的帮助,不做任何更改)

其次,我的建议是避免在函数中静态定义服务器(对于
-All
)。只需获取一个接受数组的
-ServerName
参数,并让函数检查其中的任何内容。在PowerShell中,接受单个项目和阵列时,很容易实现这一点。使用高级功能,这也可以在管道上工作,只需进行最小的额外更改

这将使您的函数只剩下一个参数,同时让它保持同样的通用性。我的函数版本如下所示:

function MyFunc
{
   [CmdletBinding()]
   PARAM(
      [Parameter(
          ValueFromRemainingArguments=$true,
          ValueFromPipeline=$true
      )]
      [string[]]
      $ServerName
   )

   Process {
       foreach ($server in $ServerName) {
           # check this particular server
       }
   }
}
Get-Content -Path 'C:\List\Servers.txt' | MyFunc
以下是调用此函数的不同方法,所有函数都可以使用:

MyFunc 'SomeServer'
MyFunc 'Server4' 'Server5' 'Server6' # Spaces
MyFunc -ServerName 'ThisServer'
MyFunc -ServerName 'ThatServer','ThisServer','OtherServer'
'CoolServer' | MyFunc
'Server1','Server2','Server3' | MyFunc
函数的调用方可以使用预定义服务器列表

如果确实要预定义服务器列表,可以将它们放在单独的文本文件中,每行一个,然后执行以下操作:

function MyFunc
{
   [CmdletBinding()]
   PARAM(
      [Parameter(
          ValueFromRemainingArguments=$true,
          ValueFromPipeline=$true
      )]
      [string[]]
      $ServerName
   )

   Process {
       foreach ($server in $ServerName) {
           # check this particular server
       }
   }
}
Get-Content -Path 'C:\List\Servers.txt' | MyFunc
这就是在您的功能中支持管道的美妙之处

您还可以定义一个函数,该函数为您提供列表,其实现可能会在以后更改:

function Get-DefaultServerList {
[CmdletBinding()]
param()

    @(
        'Server1'
        'Server2'
        'Server3'
    )

}
然后你可以做:

Get-DefaultServerList | MyFunc
重点是什么?您可以在以后更改该函数的定义,以(比如)从文件、web服务、注册表中获取其列表,无论什么。

补充一些一般性建议:

  • 要实现互斥参数,请使用参数集

    • 看到和
  • 不要强制使用
    [switch]
    参数,除非这些参数用于选择(唯一地意味着使用)一个已定义的参数集。敬请见谅

    • 对于整个函数,在所有参数集上,
      [switch]
      es应该是可选的(并且应该始终默认为
      $False
      ,这是隐式的)
      换句话说:应该至少有一个参数集不需要用户通过开关
  • 使用基于注释的帮助,您的高级功能不仅可以自动使用
    获取帮助
    ,还可以支持标准的
    -?
    参数来调用基本帮助(要获得更详细的帮助,必须使用
    获取帮助

  • 尽可能避免
    value from remainingarguments
    ,PowerShell传递开放式相关值集的方法是使用数组参数,将值以逗号分隔传递给数组参数

根据以上建议,这里是函数的重写形式

为了与标准cmdlet保持一致,我已将
-ServerName
参数更改为
-ComputerName

Briantist的观点是不要将服务器名称硬编码到函数中,但我在这里这样做是为了说明参数集的使用:
您可以使用
-All
或至少一个服务器名调用
MyFunc

使用参数集可确保不能同时执行这两项操作

<#
.SYNOPSIS
One-line description.

.DESCRIPTION
More detailed description

.PARAMETER All
Targets all servers.

.PARAMETER ComputerName
The name(s) of the server(s) to targets.

.EXAMPLE
> MyFunc -All
#>
function MyFunc {
  [CmdletBinding(DefaultParameterSetName='Given', PositionalBinding=$False)]
  param(
    [Parameter(ParameterSetName='Given', Position=0, Mandatory, ValueFromPipeline)]
    [string[]] $ComputerName
    ,
    [Parameter(ParameterSetName='All', Mandatory)]
    [switch] $All
  )

  begin {
    # If -All was specified, determine the set of servers.
    if ($PSCmdlet.ParameterSetName -eq 'All') {
      $ComputerName = 'Server1', 'Server2'
    }
  }

  process {
    foreach ($server in $ComputerName) {
      $server # process each server
    }
  }

}
MyFunc-All
#>
函数MyFunc{
[CmdletBinding(DefaultParameterSetName='Given',PositionBinding=$False)]
param(
[参数(ParameterSetName='Given',Position=0,必填,ValueFromPipeline)]
[字符串[]]$ComputerName
,
[参数(ParameterSetName='All',必填)]
[切换]$All
)
开始{
#如果指定了-All,请确定服务器集。
if($PSCmdlet.ParameterSetName-eq'All'){
$ComputerName='Server1','Server2'
}
}
过程{
foreach($ComputerName中的服务器){
$server#处理每台服务器
}
}
}

做得很好;可能值得一提的是,基于注释的帮助还为您提供自动的
-?
参数支持。这一支持:
MyFunc'Server1'、'Server2'、'Server3'
将无法与
ValueFromRemainingArguments=$true
@PetSerAl正常工作:很好;详细说明:如果将其余参数指定为数组(逗号分隔),则必须显式使用参数名,即:
MyFunc-ServerName Server1、Server2、Server3
。不带参数名,仅限