提问人:Nasir 提问时间:2/14/2011 最后编辑:mklement0Nasir 更新时间:5/18/2022 访问量:873255
如何在 PowerShell 中将多个参数传递到函数中?
How do I pass multiple parameters into a function in 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 之间似乎是一致的,所以很明显,我做错了什么。谁能指出具体是什么?
答:
对 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
评论
(1,2,3)
$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
Powershell
是一种 shell 语言,shell 语言通常使用空格作为标记分隔符。我不会说这里有所不同,它与其他系统默认 shell(如 、 、 等)一致。Powershell
cmd
sh
bash
调用 PowerShell 函数时不带括号,也不使用逗号作为分隔符。尝试使用:
test "ABC" "DEF"
在 PowerShell 中,逗号 (,) 是数组运算符,例如
$a = "one", "two", "three"
它设置为具有三个值的数组。$a
我不知道你在用这个函数做什么,但看看使用“param”关键字。它对于将参数传递到函数中的功能要强大得多,并使其更加用户友好。以下是Microsoft关于它的一篇过于复杂的文章的链接。它并不像文章听起来那么复杂。
另外,这是本网站上一个问题的示例:
一探究竟。
评论
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 函数。
评论
@
@()
@(1, 2)
正确答案已经提供,但这个问题似乎很普遍,足以让那些想要了解其中微妙之处的人需要一些额外的细节。
我本来会添加这个作为评论,但我想包括一个插图 - 我从我的 PowerShell 函数快速参考图表上撕下了它。这假设函数 f 的签名是:f($a, $b, $c)
因此,可以使用空格分隔的位置参数或与顺序无关的命名参数来调用函数。其他陷阱表明,您需要了解逗号、括号和空格。
有关进一步阅读,请参阅我的文章 Down the Rabbit Hole: A Study in PowerShell Pipelines, Functions, and Parameters。本文还包含指向快速参考/挂图的链接。
我之前说过以下几点:
常见的问题是使用单数形式,这是不正确的。它应始终为复数形式。$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 / 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()
{
Param ([string]$ParamName);
# Operations
}
评论
如果您不知道(或关心)您将传递给函数多少参数,您也可以使用非常简单的方法,例如;
代码:
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 函数应该使用批准的动词(动词-名词作为函数名称)。 名称的谓词部分标识 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
Get-Node
Retrieve-Node
Receive-Node
[Alias('something')]
function Test-Script { [CmdletBinding()] [Alias('tst')] Param() Write-Output "This function works."}
Function Test {
Param([string]$arg1, [string]$arg2)
Write-Host $arg1
Write-Host $arg2
}
这是一个恰当的声明。params
请参见about_Functions_Advanced_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"
Position=0
name
id
int
string
)
我在这里没有提到它,但是分散你的参数是一个有用的选择,如果你正在动态地构建命令的参数(而不是使用 )。您可以使用位置参数的数组和命名参数的哈希表进行 splat。以下是一些示例:Invoke-Expression
注意:您可以相对轻松地将位置 splats 与外部命令参数一起使用,但命名 splats 对外部命令不太有用。它们有效,但程序必须接受该格式的参数,因为每个参数都与哈希表键/值对相关。此类软件的一个例子是来自 Windows 的 Chocolatey 包管理器的命令。
-Key:Value
choco
使用数组进行 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
评论
Test "ABC" "DEF"