如何在 PowerShell 中将多个参数传递到函数中?

How do I pass multiple parameters into a function in PowerShell?

提问人:Nasir 提问时间:2/14/2011 最后编辑:mklement0Nasir 更新时间:5/18/2022 访问量:873255

问:

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

快速测试脚本:

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 之间似乎是一致的,所以很明显,我做错了什么。谁能指出具体是什么?

PowerShell 语法 参数传递

评论

7赞 Ranadip Dutta 2/17/2017
你只是这样称呼:Test "ABC" "DEF"
2赞 Urasquirrel 5/13/2023
恕我直言,这个问题和答案有如此多的赞成票这一事实表明,当前的实现是......不直观。

答:

742赞 x0n 2/14/2011 #1

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

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

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

评论

34赞 Ashley 12/13/2013
最终帮助我“坚持”这一点的最重要的事情是最后一句话:“括号参数仅在 .NET 方法中使用。
4赞 sam yi 3/19/2014
我更喜欢使用分隔的 paranthesis 和逗号分隔。.是否可以在 PowerShell 中执行此操作?
10赞 x0n 3/19/2014
@samyi号将 a 传递给函数可以有效地被视为数组;一个参数。如果要使用 OO 方法样式参数,请使用 modules:(1,2,3)$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
6赞 codewario 6/7/2019
Powershell是一种 shell 语言,shell 语言通常使用空格作为标记分隔符。我不会说这里有所不同,它与其他系统默认 shell(如 、 、 等)一致。Powershellcmdshbash
1赞 x0n 5/12/2021
@ShayanZafar正如我在原来的帖子中所说,该语法适用于 .NET 框架调用。只有本机 powershell 函数/cmdlet 使用空格。
56赞 Todd 2/14/2011 #2

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

test "ABC" "DEF"

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

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

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

5赞 Rodney Fisk 2/16/2011 #3

我不知道你在用这个函数做什么,但看看使用“param”关键字。它对于将参数传递到函数中的功能要强大得多,并使其更加用户友好。以下是Microsoft关于它的一篇过于复杂的文章的链接。它并不像文章听起来那么复杂。

参数用法

另外,这是本网站上一个问题的示例:

一探究竟。

评论

0赞 Nasir 2/16/2011
谢谢你的回答。但是,我在调用该函数时遇到了问题。函数是用 param 声明还是不用它都无关紧要。
22赞 John B 11/4/2011 #4
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test "ABC" "DEF"
8赞 RaSor 12/10/2012 #5

如果您尝试:

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 函数。

评论

4赞 n0rd 10/13/2013
参数不分隔参数。它们定义数组。
1赞 VertigoRay 3/12/2019
Parens 不定义数组,而是定义一个组,PowerShell 可以将其解释为数组。数组在前导括号 () 之前用 at 符号 () 定义,如下所示:;或这个包含两个数字的数组:.@@()@(1, 2)
340赞 Michael Sorens 4/8/2013 #6

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

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

Syntax pitfalls of a function call

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

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

3赞 Eric 11/8/2013 #7

我之前说过以下几点:

常见的问题是使用单数形式,这是不正确的。它应始终为复数形式。$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

评论

4赞 Nasir 11/8/2013
谢谢我的朋友,但是,你晚了几年:-)这里的前三个答案已经充分解决了这个问题。我可以建议前往“未回答”部分并尝试其中一些问题吗?
4赞 user3222697 1/22/2014 #8
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC") ("DEF")
14赞 Ryan Shillington 8/27/2015 #9

如果你是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

评论

3赞 Peter Mortensen 10/26/2019
Java,C++,Ruby和Python不是来自本世纪(只有C#),假设公历(尽管有些比其他历法发展得更多)。
0赞 Ryan Shillington 10/27/2019
嘿。@PeterMortensen你的论点是我应该说“从本世纪或上个世纪选择一种语言”?:-)
1赞 Kaushal Khamar 5/14/2016 #10

您也可以在函数中传递参数,如下所示:

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

评论

4赞 Nasir 5/16/2016
这将是为函数定义参数,最初的问题是关于如何在调用函数时指定参数。
12赞 Draino 1/27/2017 #11

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

代码

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
}
15赞 Martin Brandl 4/21/2017 #12

因为这是一个经常看到的问题,所以我想提一下,PowerShell 函数应该使用批准的动词(动词-名词作为函数名称)。 名称的谓词部分标识 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"
}

要将参数传递给函数,可以使用以下位置

Test-Script "Hello" "World"

或者指定参数名称

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

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


我建议在使用多个参数时始终传递参数名称,因为这更具可读性

评论

0赞 Keith Langmead 9/21/2018
仅供参考,批准的动词列表链接不再有效,但现在可以在这里找到 - learn.microsoft.com/en-us/powershell/developer/cmdlet/...
0赞 Martin Brandl 9/22/2018
@KeithLangmead 谢谢基思,我也更新了我的答案。
1赞 Peter Mortensen 10/26/2019
“动词-名词”在动词和名词中都大写?也许改变答案以更明确地说明它?
1赞 Martin Brandl 2/7/2020
好吧,请考虑公开一个 cmdlet。对我们来说很清楚,我们必须调用 ,而不是 、 或 .....Get-NodeGet-NodeRetrieve-NodeReceive-Node
1赞 IT M 9/2/2020
在 Param() 部分之前添加也很有意义。这允许使用非动词认可的函数(例如gci,ls,dir,cd等)。例:[Alias('something')]function Test-Script { [CmdletBinding()] [Alias('tst')] Param() Write-Output "This function works."}
2赞 Serhii Kimlyk 5/16/2017 #13
Function Test {
    Param([string]$arg1, [string]$arg2)

    Write-Host $arg1
    Write-Host $arg2
}

这是一个恰当的声明。params

请参见about_Functions_Advanced_Parameters

它确实有效。

97赞 user2233949 6/24/2017 #14

这里有一些很好的答案,但我想指出其他几件事。函数参数实际上是 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

即使提供了第二个示例,第一个示例也有效,因为我们显式使用了参数名称。不过,第二个示例基于位置工作,因此需要是第一个示例。如果可能的话,我总是尝试定义位置,以便两个选项都可用。NameName

PowerShell 还具有定义参数集的功能。它用它来代替方法重载,并且同样非常有用:

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扩展名。($_ 是指在脚本块中的参数。如果需要该级别,您还可以传入更大的多行脚本块,或者像我在这里所做的那样使用多个脚本块。它非常有用,可以提供干净的功能和直观的例外。

评论

3赞 Mister_Tom 11/23/2017
+1 用于演示函数调用风格。为了提高可读性,更多 PS 脚本代码应遵循此模式。My_Function -NamedParamater "ParamValue"
1赞 Jason Lee 7/10/2021
有两个是错别字吗?Position=0
1赞 user2233949 7/12/2021
不,这不是错别字。当您使用参数集时,情况就是如此,参数集基本上只是方法重载。因此,在这种情况下,可以传递任何一个 OR,但不能同时传递两者。由于两者都是位置 0,因此,如果未指定参数名称,PowerShell 将根据类型确定要使用的参数。(一个是,一个是nameidintstring)
7赞 codewario 6/7/2019 #15

我在这里没有提到它,但是分散你的参数是一个有用的选择,如果你正在动态地构建命令的参数(而不是使用 )。您可以使用位置参数的数组和命名参数的哈希表进行 splat。以下是一些示例:Invoke-Expression

注意:您可以相对轻松地将位置 splats 与外部命令参数一起使用,但命名 splats 对外部命令不太有用。它们有效,但程序必须接受该格式的参数,因为每个参数都与哈希表键/值对相关。此类软件的一个例子是来自 Windows 的 Chocolatey 包管理器的命令。-Key:Valuechoco

使用数组进行 Splat(位置参数)

使用位置参数的测试连接

Test-Connection www.google.com localhost

带阵列展开

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

请注意,在展开时,我们用 而不是 .使用 Hashtable 进行 splat 时也是如此。@$

使用哈希表(命名参数)进行 Splat

使用命名参数的测试连接

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

使用哈希表展开

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

同时对位置参数和命名参数进行 Splat

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

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

将 Splatting 数组和哈希表结合在一起

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

评论

0赞 Mike Q 7/25/2020
这对我不起作用,但我做了这样的事情 $Results = GetServerInformation $Servers $ServerNames
0赞 codewario 7/25/2020
展开是用于获取哈希表或数组并将它们作为命名参数或位置参数展开到另一个命令的语法,而不是像您所做的那样将参数直接传递给命令。如果您发布有关您的 splat 不起作用的问题,有人可能会对您的特定问题有所了解。