如果我有一个接受多个字符串参数的函数,则第一个参数似乎获取分配给它的所有数据,其余参数作为空传递。
快速测试脚本:
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(所有版本)中函数调用中的参数以空格分隔,而不是逗号分隔。此外,如果 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
已经提供了正确的答案,但是这个问题似乎很普遍,足以为那些想要了解其中细微之处的人提供一些额外的细节。
我会添加这个作为评论,但我想包括一个插图——我把它从我关于 PowerShell 函数的快速参考图表上撕下来了。这假设函数 f 的签名是 f($a, $b, $c)
:
https://i.stack.imgur.com/2v8hD.png
因此,可以使用空格分隔的位置参数或与顺序无关的命名参数调用函数。其他陷阱表明您需要了解逗号、括号和空格。
如需进一步阅读,请参阅我的文章Down the Rabbit Hole: A Study in PowerShell Pipelines, Functions, and Parameters。该文章还包含指向快速参考/挂图的链接。
这里有一些很好的答案,但我想指出其他一些事情。函数参数其实是 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 还具有定义参数集的能力。它使用它来代替方法重载,并且非常有用:
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 扩展名。 ($_ 指的是参数,当在您的脚本块中时。)如果需要该级别,您还可以传入更大的多行脚本块,或者像我在这里所做的那样使用多个脚本块。它非常有用,可以提供很好的干净功能和直观的异常。
My_Function -NamedParamater "ParamValue"
函数调用样式。这是更多 PS 脚本代码应遵循的模式以提高可读性。
Position=0
是错字吗?
name
或 id
,但不能同时通过。由于两者都是位置 0,如果您不指定参数名称,PowerShell 将根据类型确定您使用的是哪一个。 (一个是 int
,一个是 string
)
您调用 PowerShell 函数时不带括号,也不使用逗号作为分隔符。尝试使用:
test "ABC" "DEF"
在 PowerShell 中,逗号 (,) 是一个数组运算符,例如
$a = "one", "two", "three"
它将 $a
设置为具有三个值的数组。
Function Test([string]$arg1, [string]$arg2)
{
Write-Host "`$arg1 value: $arg1"
Write-Host "`$arg2 value: $arg2"
}
Test "ABC" "DEF"
如果您是 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
如果您不知道(或不关心)将传递给函数的参数数量,您也可以使用一种非常简单的方法,例如;
代码:
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
}
因为这是一个经常被查看的问题,所以我想提一下 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"
}
要将参数传递给函数,您可以使用以下位置:
Test-Script "Hello" "World"
或者您指定参数名称:
Test-Script -arg1 "Hello" -arg2 "World"
不像在 C# 中调用函数时那样使用括号。
我建议在使用多个参数时始终传递参数名称,因为这样更具可读性。
Get-Node
cmdlet。很明显,我们必须调用 Get-Node
,而不是 Retrieve-Node
,也不是 Receive-Node
,也不是......
[Alias('something')]
也很有意义。这允许使用未经动词批准的功能(例如 gci、ls、dir、cd ...)。示例:function Test-Script { [CmdletBinding()] [Alias('tst')] Param() Write-Output "This function works."}
如果你试试:
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 函数。
@
) 定义,例如这个空数组:@()
;或者这个数组有两个数字:@(1, 2)
。
Function Test([string]$arg1, [string]$arg2)
{
Write-Host "`$arg1 value: $arg1"
Write-Host "`$arg2 value: $arg2"
}
Test("ABC") ("DEF")
我不知道你在用这个函数做什么,但是看看使用 'param' 关键字。将参数传递给函数的功能要强大得多,并且更加用户友好。下面是微软关于它的一篇过于复杂的文章的链接。它并不像文章所说的那么复杂。
此外,以下是此网站上 a question 的示例:
一探究竟。
我没有看到这里提到它,但是 splatting 您的参数是一个有用的替代方案,如果您动态地构建命令的参数(而不是使用 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
请注意,在 splatting 时,我们使用 @ 而不是 $ 来引用 splatted 变量。使用 Hashtable 进行 splat 时也是如此。
Splat with Hashtable(命名参数)
使用命名参数的测试连接
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
将数组和散列表放在一起
$argumentHash = @{
Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
我之前说过以下几点:
常见问题是使用不正确的单数形式 $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 {
Param([string]$arg1, [string]$arg2)
Write-Host $arg1
Write-Host $arg2
}
这是一个正确的 params
声明。
请参阅 about_Functions_Advanced_Parameters。
它确实有效。
您也可以在这样的函数中传递参数:
function FunctionName()
{
Param ([string]$ParamName);
# Operations
}
不定期副业成功案例分享
(1,2,3)
传递给函数被有效地视为数组;一个论点。如果您想使用 OO 方法样式参数,请使用模块:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
Powershell
是一种 shell 语言,shell 语言通常使用空格作为标记分隔符。我不会说Powershell
在这里有所不同,它与其他系统默认外壳程序(如cmd
、sh
、bash
等)完全一致。