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

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

一个快速测试脚本:

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

805336 次浏览

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

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


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

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

test "ABC" "DEF"

在PowerShell中,逗号(,)是一个数组操作符,例如:

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

它将$a设置为一个包含三个值的数组。

我不知道你在用这个函数做什么,但看看使用'param'关键字。它在向函数传递参数方面更加强大,并使其更加用户友好。下面是来自微软的一篇过于复杂的文章的链接。这并不像文章说的那么复杂。

参数用法 .

另外,这里有一个来自这个网站上一个问题的例子:

来看看。

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


Test "ABC" "DEF"

如果你尝试:

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

正确的答案已经给出了,但是这个问题似乎很普遍,对于那些想要理解其中微妙之处的人来说,有必要提供一些额外的细节。

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

函数调用的语法缺陷 .

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

欲进一步阅读,请参阅我的文章下到兔子洞:PowerShell管道,函数和参数的研究。这篇文章还包含了快速参考/挂图的链接。

我刚才说过:

常见的问题是使用单数形式$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
Function Test([string]$arg1, [string]$arg2)
{
Write-Host "`$arg1 value: $arg1"
Write-Host "`$arg2 value: $arg2"
}


Test("ABC") ("DEF")

如果你是c# / Java / c++ / Ruby / Python /自本世纪选择一种语言的开发人员,你想用逗号调用你的函数,因为这是你一直在做的,那么你需要这样的东西:

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

现在电话:

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

你们会看到

arg1 = ABC, and arg2 = DEF

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

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

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

# EYZ0:

function FunctionName()
{
Write-Host $args
}

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

FunctionName a b c 1 2 3

输出

a b c 1 2 3

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

下面是另一个真实世界的例子(为tracert命令创建一个函数,我讨厌不得不记住截断的名称);

# EYZ0:

Function traceroute
{
Start-Process -FilePath "$env:systemroot\system32\tracert.exe" -ArgumentList $args -NoNewWindow
}
因为这是一个经常看到的问题,我想提一下PowerShell函数应该使用批准的动词 (Verb-Noun作为函数名)。 名称的动词部分标识cmdlet执行的操作。名称的名词部分标识执行动作的实体。这条规则简化了用于高级PowerShell用户使用cmdlet。< / p >

此外,你可以指定参数是否为强制性的和参数的位置:

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#中调用一个函数一样。


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

Function Test {
Param([string]$arg1, [string]$arg2)


Write-Host $arg1
Write-Host $arg2
}

这是一个正确的params声明。

看到# EYZ0。

它确实有效。

这里有一些很好的答案,但我想指出一些其他的事情。函数参数实际上是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。第二个示例基于位置工作,因此Name需要放在第一位。在可能的情况下,我总是尝试定义位置,以便两种选项都可用。

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扩展名。($_指的是参数,当在你的脚本块。)如果需要,您还可以传入更大的多行脚本块,或者像我在这里所做的那样使用多个脚本块。它非常有用,可以生成非常干净的函数和直观的异常。

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

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

Splat With Arrays(位置参数)

使用位置参数测试连接

Test-Connection www.google.com localhost

与阵列溅射

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

注意,在飞溅时,我们用@而不是$引用飞溅的变量。当使用Hashtable进行splat时也是一样的。

Splat With Hashtable (Named Arguments)

使用命名参数测试连接

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