【问题标题】:How do I pass multiple parameters into a function in PowerShell?如何将多个参数传递给 PowerShell 中的函数?
【发布时间】:2011-06-26 16:06:00
【问题描述】:

如果我有一个函数接受多个字符串参数,第一个参数似乎获取分配给它的所有数据,其余参数作为空传入。

快速测试脚本:

Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC", "DEF")

生成的输出是

$arg1 value: ABC DEF
$arg2 value: 

正确的输出应该是:

$arg1 value: ABC
$arg2 value: DEF

这似乎在多台机器上的 v1 和 v2 之间是一致的,所以很明显,我做错了什么。谁能指出具体是什么?

【问题讨论】:

  • 你可以这样调用:Test "ABC" "DEF"

标签: powershell syntax parameter-passing


【解决方案1】:

我没有看到这里提到它,但是 splatting 你的参数是一个有用的替代方案,如果你动态地构建命令的参数(而不是使用 Invoke-Expression),它会变得特别有用。您可以为位置参数使用数组,为命名参数使用哈希表。以下是一些示例:

注意:您可以将位置 splats 与外部命令一起使用,但不能使用命名参数。即使外部命令可能具有“命名”参数
(例如,ping -n 1 server.domain.tld-n 将是“命名”),但从 PowerShell 的上下文来看,外部命令参数被视为位置参数,因为 PowerShell 无法知道外部命令command 定义了它的参数。

Splat 与数组(位置参数)

使用位置参数测试连接

Test-Connection www.google.com localhost

使用数组飞溅

$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentArray

请注意,在 splatting 时,我们使用 @ 而不是 $ 来引用 splatted 变量。使用 Hashtable 进行 splat 时也是如此。

带有哈希表的 Splat(命名参数)

使用命名参数的测试连接

Test-Connection -ComputerName www.google.com -Source localhost

带有散列表

$argumentHash = @{
  ComputerName = 'www.google.com'
  Source = 'localhost'
}
Test-Connection @argumentHash

同时显示位置参数和命名参数

位置参数和命名参数的测试连接

Test-Connection www.google.com localhost -Count 1

将数组和哈希表放在一起

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray

【讨论】:

  • 这对我不起作用,但我做了类似的事情 $Results = GetServerInformation $Servers $ServerNames
  • Splatting 是用于获取哈希表或数组并将它们作为命名参数或位置参数展开到另一个命令的语法,而不是像您所做的那样将参数直接传递给命令。如果您发布有关您的 splat 无法正常工作的问题,那么某人可能能够对您的特定问题有所了解。
【解决方案2】:

PowerShell(所有版本)中函数调用中的参数以空格分隔,而不是逗号分隔。此外,括号是完全不必要的,如果 Set-StrictMode -Version 2 或更高版本处于活动状态,则会在 PowerShell 2.0(或更高版本)中导致解析错误。带括号的参数仅在 .NET 方法中使用。

function foo($a, $b, $c) {
   "a: $a; b: $b; c: $c"
}

ps> foo 1 2 3
a: 1; b: 2; c: 3

【讨论】:

  • 最后让我“坚持”这一点的最重要的事情是最后一句话:“括号中的参数仅用于 .NET 方法。”
  • 我更喜欢使用括号和逗号分隔.. 是否可以在 powershell 中执行此操作?
  • @samyi 否。将(1,2,3) 传递给函数被有效地视为数组;一个论点。如果要使用 OO 方法样式参数,请使用模块:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
  • Powershell 是一种 shell 语言,shell 语言通常使用空格作为标记分隔符。我不会说 Powershell 在这里有所不同,它与其他系统默认 shell 如cmdshbash 等是一致的。
  • @ShayanZafar 正如我在原始帖子中所说,该语法适用于 .NET 框架调用。只有本机 powershell 函数/cmdlet 使用空格。
【解决方案3】:

因为这是一个经常被查看的问题,我想提一下,PowerShell 函数应该使用approved verbs动词-名词作为函数名)。 名称的动词部分标识 cmdlet 执行的操作。名称的名词部分标识对其执行操作的实体。此规则简化高级 PowerShell 用户对 cmdlet 的使用。

此外,您还可以指定参数是否为强制以及参数的位置

function Test-Script
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$arg1,

        [Parameter(Mandatory=$true, Position=1)]
        [string]$arg2
    )

    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

要将参数传递给函数,您可以使用 position

Test-Script "Hello" "World"

或者您指定参数名称

Test-Script -arg1 "Hello" -arg2 "World"

不要像在 C# 中调用函数时那样使用括号


我建议总是在使用多个参数时传递参数名称,因为这样更可读

【讨论】:

  • 仅供参考,已批准的动词列表链接不再有效,但现在可以在这里找到 - docs.microsoft.com/en-us/powershell/developer/cmdlet/…
  • @KeithLangmead 谢谢基思,我也更新了我的答案。
  • “动词-名词”在动词和名词中都大写?也许将答案更改为更明确?
  • 好吧,假设您公开了一个Get-Node cmdlet。我们很清楚,我们必须调用Get-Node,而不是Retrieve-Node,也不是Receive-Node,也不是.....
  • 在 Param() 部分之前添加 [Alias('something')] 也很有意义。这允许使用未经动词批准的功能(例如 gci、ls、dir、cd ...)。示例:function Test-Script { [CmdletBinding()] [Alias('tst')] Param() Write-Output "This function works."}
【解决方案4】:

这里有一些很好的答案,但我想指出其他一些事情。函数参数其实是 PowerShell 大放异彩的地方。例如,您可以在高级函数中使用命名参数或位置参数,如下所示:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=1)]
         [int] $Id
    )
}

然后您可以通过指定参数名称来调用它,或者您可以只使用位置参数,因为您明确定义了它们。所以这两种方法都可以:

Get-Something -Id 34 -Name "Blah"
Get-Something "Blah" 34

即使Name 第二个提供,第一个示例仍然有效,因为我们明确使用了参数名称。第二个示例基于位置,因此Name 需要成为第一个。如果可能,我总是尝试定义位置,以便两个选项都可用。

PowerShell 还具有定义参数集的能力。它使用 this 代替方法重载,而且非常有用:

function Get-Something
{
    [CmdletBinding(DefaultParameterSetName='Name')]
    Param
    (
         [Parameter(Mandatory=$true, Position=0, ParameterSetName='Name')]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=0, ParameterSetName='Id')]
         [int] $Id
    )
}

现在该函数将采用名称或 id,但不能同时采用两者。您可以按位置或按名称使用它们。由于它们是不同的类型,PowerShell 会弄清楚。所以所有这些都可以工作:

Get-Something "some name"
Get-Something 23
Get-Something -Name "some name"
Get-Something -Id 23

您还可以将其他参数分配给各种参数集。 (这显然是一个非常基本的示例。)在函数内部,您可以确定哪个参数集与 $PsCmdlet.ParameterSetName 属性一起使用。例如:

if($PsCmdlet.ParameterSetName -eq "Name")
{
    Write-Host "Doing something with name here"
}

然后,在相关的旁注中,PowerShell 中还有参数验证。这是我最喜欢的 PowerShell 功能之一,它使函数内的代码非常干净。您可以使用许多验证。几个例子是:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [ValidatePattern('^Some.*')]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=1)]
         [ValidateRange(10,100)]
         [int] $Id
    )
}

在第一个示例中,ValidatePattern 接受一个正则表达式,以确保提供的参数与您期望的匹配。如果没有,就会抛出一个直观的异常,告诉你到底哪里出了问题。因此,在该示例中,“Something”可以正常工作,但“Summer”无法通过验证。

ValidateRange 确保参数值在您期望的整数范围之间。所以 10 或 99 会起作用,但 101 会抛出异常。

另一个有用的是 ValidateSet,它允许您显式定义一个可接受值的数组。如果输入其他内容,将引发异常。还有其他的,但可能最有用的一个是ValidateScript。这需要一个必须评估为 $true 的脚本块,所以没有限制。例如:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [ValidateScript({ Test-Path $_ -PathType 'Leaf' })]
         [ValidateScript({ (Get-Item $_ | select -Expand Extension) -eq ".csv" })]
         [string] $Path
    )
}

在此示例中,我们确信 $Path 不仅存在,而且它是一个文件(与目录相反)并且具有 .csv 扩展名。 ($_ 指的是参数,当在您的脚本块中时。)如果需要该级别,您还可以传递更大的多行脚本块,或者像我在这里所做的那样使用多个脚本块。它非常有用,可以提供漂亮的干净功能和直观的异常。

【讨论】:

  • +1 用于演示 My_Function -NamedParamater "ParamValue" 函数调用样式。为了便于阅读,更多的 PS 脚本代码应该遵循这种模式。
  • 有两个Position=0 是错字吗?
  • 不,这不是错字。当您使用参数集时就是这种情况,这些参数集基本上只是方法重载。所以在这种情况下,nameid 可以通过,但不能同时通过。由于两者都是位置 0,如果您不指定参数名称,PowerShell 将根据类型确定您使用的是哪一个。 (一个是int,一个是string
【解决方案5】:
Function Test {
    Param([string]$arg1, [string]$arg2)

    Write-Host $arg1
    Write-Host $arg2
}

这是一个正确的params 声明。

about_Functions_Advanced_Parameters

它确实有效。

【讨论】:

    【解决方案6】:

    如果您不知道(或不关心)将传递给函数的参数数量,您也可以使用非常简单的方法,例如:

    代码

    function FunctionName()
    {
        Write-Host $args
    }
    

    这将打印出所有参数。例如:

    FunctionName a b c 1 2 3
    

    输出

    a b c 1 2 3
    

    我发现这在创建使用可能具有许多不同(和可选)参数的外部命令的函数时特别有用,但依赖于所述命令来提供有关语法错误等的反馈。

    这是另一个真实的例子(为 tracert 命令创建一个函数,我讨厌必须记住截断的名称);

    代码

    Function traceroute
    {
        Start-Process -FilePath "$env:systemroot\system32\tracert.exe" -ArgumentList $args -NoNewWindow
    }
    

    【讨论】:

      【解决方案7】:

      您也可以像这样在函数中传递参数

      function FunctionName()
      {
          Param ([string]$ParamName);
          # Operations
      }
      

      【讨论】:

      • 那是给函数定义参数,原来的问题是调用函数时如何指定参数。
      【解决方案8】:

      如果您是 C# / Java / C++ / Ruby / Python / Pick-A-Language-From-This-Century 开发人员,并且您想用逗号调用您的函数,因为这是您一直以来的做法,那么你需要这样的东西:

      $myModule = New-Module -ascustomobject { 
          function test($arg1, $arg2) { 
              echo "arg1 = $arg1, and arg2 = $arg2"
          }
      }
      

      现在打电话:

      $myModule.test("ABC", "DEF")
      

      你会看到

      arg1 = ABC, and arg2 = DEF
      

      【讨论】:

      • Java、C++、Ruby 和 Python 不是来自本世纪(仅 C#),假设 Gregorian calendar(尽管有些比其他的进化得更多)。
      • 嘿。 @PeterMortensen,您的论点是我应该说“从本世纪或上世纪选择一种语言”? :-)
      【解决方案9】:

      我之前说过:

      常见的问题是使用单数形式$arg,这是不正确的。它应该始终是复数形式,如 $args

      问题不在于。事实上,$arg 可以是其他任何东西。问题是逗号和括号的使用。

      我运行以下有效的代码,输出如下:

      代码:

      Function Test([string]$var1, [string]$var2)
      {
          Write-Host "`$var1 value: $var1"
          Write-Host "`$var2 value: $var2"
      }
      

      测试“ABC”“DEF”

      输出:

      $var1 value: ABC
      $var2 value: DEF
      

      【讨论】:

      • 谢谢你,我的朋友,但是,你晚了几年:-) 这里的前三个答案已经充分解决了这个问题。我可以建议前往未回答部分并尝试其中一些问题吗?
      【解决方案10】:

      已经提供了正确的答案,但是这个问题似乎很普遍,需要一些额外的细节来帮助那些想要了解其中微妙之处的人。

      我会添加这个作为评论,但我想包括一个插图——我把它从我的 PowerShell 函数快速参考图表上撕下来。这假设函数 f 的签名是 f($a, $b, $c):

      因此,可以使用空格分隔的位置参数或与顺序无关的命名参数调用函数。其他陷阱表明您需要了解逗号、括号、空格。

      如需进一步阅读,请参阅我的文章Down the Rabbit Hole: A Study in PowerShell Pipelines, Functions, and Parameters。该文章还包含指向快速参考/挂图的链接。

      【讨论】:

        【解决方案11】:

        如果你尝试:

        PS > Test("ABC", "GHI") ("DEF")
        

        你得到:

        $arg1 value: ABC GHI
        $arg2 value: DEF
        

        所以你看到括号分隔参数


        如果你尝试:

        PS > $var = "C"
        PS > Test ("AB" + $var) "DEF"
        

        你得到:

        $arg1 value: ABC
        $arg2 value: DEF
        

        现在您可以发现括号的一些直接用途 - 空格不会成为下一个参数的分隔符 - 而是有一个 eval 函数。

        【讨论】:

        • Parens 不分隔参数。他们定义了数组。
        • Parens 不定义数组,它们定义了一个组,powershell 可以将其解释为数组。数组在前导括号之前用 at 符号 (@) 定义,例如这个空数组:@();或者这个数组有两个数字:@(1, 2).
        【解决方案12】:

        我不知道你在用这个函数做什么,但是看看使用 'param' 关键字。将参数传递给函数的功能要强大得多,并且更加用户友好。下面是微软关于它的一篇过于复杂的文章的链接。它并不像文章所说的那么复杂。

        Param Usage

        另外,这里有一个来自本网站a question 的示例:

        检查一下。

        【讨论】:

        • 感谢您的回答。但是,我在调用该函数时遇到了问题。函数是用参数声明还是不带参数都没有关系。
        【解决方案13】:

        您调用 PowerShell 函数时不带括号,也不使用逗号作为分隔符。尝试使用:

        test "ABC" "DEF"
        

        在 PowerShell 中,逗号 (,) 是一个数组运算符,例如

        $a = "one", "two", "three"
        

        它将$a 设置为具有三个值的数组。

        【讨论】:

          【解决方案14】:
          Function Test([string]$arg1, [string]$arg2)
          {
              Write-Host "`$arg1 value: $arg1"
              Write-Host "`$arg2 value: $arg2"
          }
          
          Test("ABC") ("DEF")
          

          【讨论】:

            【解决方案15】:
            Function Test([string]$arg1, [string]$arg2)
            {
                Write-Host "`$arg1 value: $arg1"
                Write-Host "`$arg2 value: $arg2"
            }
            
            Test "ABC" "DEF"
            

            【讨论】:

              猜你喜欢
              • 2023-01-26
              • 2017-07-11
              • 2023-03-26
              • 2017-09-22
              • 1970-01-01
              • 2022-10-07
              • 2023-04-04
              相关资源
              最近更新 更多