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

时间:2016-10-19 21:19:22

标签: powershell

我正在将我的一个标准功能转换为高级功能。该功能基本上与我公司的服务器对话,并返回我需要的有关我们产品的信息。目前它被编码为只扫描所有服务器,但我想将交换机添加到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
   }
}

2 个答案:

答案 0 :(得分:1)

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

首先,摆脱-Help开关。 PowerShell有(非常好)帮助语义,你应该使用它们。为您的函数定义comment-based help,然后用户可以调用Get-Help MyFunchelp MyFunc并查看您所有的函数信息。最好的部分是,如果你想要的只是参数和需要哪些参数,你不必做任何事情;为您自动生成(继续尝试现在获取您的功能的帮助,没有任何更改)。

其次,我的建议是避免在函数中静态定义服务器(对于-All)。只需要一个带有数组的-ServerName参数,然后让你的函数检查其中的内容。 PowerShell在接受单个项目和数组时非常容易。使用高级功能,这也可以在管道上工作,只需最少的额外更改。

这将使您的功能只有一个参数,同时让它保持同样多样化。我的函数版本如下所示:

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

   Process {
       foreach ($server in $ServerName) {
           # check this particular server
       }
   }
}

以下是您可以调用此功能的不同方法:

MyFunc 'SomeServer'
MyFunc 'Server4' 'Server5' 'Server6' # Spaces
MyFunc -ServerName 'ThisServer'
MyFunc -ServerName 'ThatServer','ThisServer','OtherServer'
'CoolServer' | MyFunc
'Server1','Server2','Server3' | MyFunc

该函数的调用者可以使用$PSDefaultParameterValues预先定义服务器列表。

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

Get-Content -Path 'C:\List\Servers.txt' | MyFunc

这就是在你的职能中支持管道的美妙之处!

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

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

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

}

然后你可以这样做:

Get-DefaultServerList | MyFunc

重点是什么?您可以稍后更改该函数的定义(例如)从文件,Web服务,注册表中获取其列表,等等。

答案 1 :(得分:1)

briantist's helpful answer补充一些一般性建议:

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

  • 不要将[switch]参数强制,除非它们用于选择(唯一暗示使用)一个已定义的参数集。briantist求助的提示。

    • 对于函数作为一个整体,在所有参数集中,[switch] es应可选(并且应始终默认为$False,他们隐含地做了;) 换句话说:至少应该有一个要求用户传递开关的参数集。
  • 使用基于评论的帮助可使您的高级功能不仅可以自动使用Get-Help,还可以支持标准-?参数来调用基本帮助(有关更详细的帮助,必须使用Get-Help)。

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

根据上述建议,这是您的功能的重写形式。

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

Briantist有一个关于不将服务器名称硬编码到函数中的观点,但我在这里是为了说明参数集的使用:
使用MyFunc 至少调用1个服务器名称来调用-All
使用参数集可确保您不能同时执行这两项操作。

<#
.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
    }
  }

}