简短说明
介绍如何在 PowerShell 中创建和使用函数。
长说明
函数是 PowerShell 语句的列表,其中包含分配的名称。 运行函数时,请键入函数名称。
PowerShell 定义了两种类型的函数:
-
函数是可以按名称调用的代码块。 它可能需要输入并返回输出。 函数是使用
function
关键字定义的。 -
筛选器是一种函数,旨在处理管道中的数据。 筛选器是使用
filter
关键字定义的。
可以将函数中的语句分组为四个不同的预定义脚本块之一。 这些脚本块使用关键字begin
、process
和 end
clean
。 如果不使用这些关键字,PowerShell 会将语句放在相应的代码块中。
函数也可以像 cmdlet 一样运行。 可以创建一个像 cmdlet 一样工作的函数,而无需使用 C#
编程。 有关详细信息,请参阅 about_Functions_Advanced。
重要
在脚本文件和基于脚本的模块中,必须先定义函数,然后才能调用它们。
函数语法
函数使用以下语法定义:
function [<scope:>]<name> {
param([type]$Parameter1 [,[type]$Parameter2])
dynamicparam {<statement list>}
begin {<statement list>}
process {<statement list>}
end {<statement list>}
clean {<statement list>}
}
函数包含以下各项:
-
function
关键字 - 作用域(可选)
- 选择的名称
- 任意数量的命名参数(可选),包括动态参数
- 括在大括号中的一个或多个 PowerShell 语句
{}
如果不在定义中使用其中一个关键字(begin
、、process
、end
clean
),PowerShell 会将语句放在块中end
。function
有关函数中的 dynamicparam
关键字和动态参数的详细信息,请参阅 about_Functions_Advanced_Parameters。
函数可以像以下一样简单:
function Get-PowerShellProcess { Get-Process pwsh }
定义函数后,可以像内置 cmdlet 一样使用它。 例如,若要调用新定义的 Get-PowerShellProcess
函数:
Get-PowerShellProcess
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
110 78.72 172.39 10.62 10936 1 pwsh
函数也可以像 cmdlet 一样复杂。
函数可以返回可显示、赋给变量或传递给其他函数或 cmdlet 的值。 可以使用 return
关键字返回输出。
return
关键字不会影响或抑制从函数返回的其他输出。 但是,return
关键字会在该行退出函数。 有关详细信息,请参阅 about_Return。
筛选器语法
函数的 filter
意图是提供一种简写方式,用于定义在管道中的每个对象上运行的函数。
筛选器的语法如下所示:
filter [<scope:>]<name> {<statement list>}
若要简化函数的filter
语法,请省略脚本块关键字(begin
、end
process
clean
)。 PowerShell 将语句放在块中 process
。 可以在筛选器函数中使用任何其他块,但目的是提供一种简写方式来定义一个函数,该函数只用于处理管道中的每个对象。
以下筛选器从管道获取日志条目,然后显示整个条目或仅显示条目的消息部分:
filter Get-EventMessage ([switch]$MessageOnly) {
if ($MessageOnly) { Out-Host -InputObject $_.Message }
else { $_ }
}
使用方法如下所示:
Get-WinEvent -LogName System -MaxEvents 100 | Get-EventMessage -MessageOnly
输入处理方法
本部分所述的方法称为输入处理方法。 对于函数,这三个使用函数和end
块命名process
begin
的方法。 PowerShell 7.3 添加了 clean
块进程方法。
并非必须在函数中使用其中任何一个块。 如果不使用命名块,PowerShell 会将代码置于函数的 end
块中。 但是,如果使用这些命名块中的任何一个,或定义 dynamicparam
块,则必须将所有代码放入命名块中。
以下示例显示了一个函数的轮廓,该函数包含 begin
用于一次性预处理的 process
块、管道中的块数据以及一 end
次性处理后的块。
Function Test-ScriptCmdlet {
[CmdletBinding(SupportsShouldProcess=$true)]
param ($Parameter1)
begin{}
process{}
end{}
}
begin
此块用于为函数提供可选的一次性预处理。 PowerShell 运行时会为管道中函数的每个实例使用此块中的代码一次。
process
此块用于为函数提供逐记录处理。 可以使用 process
块而无需定义其他块。
process
块执行的次数取决于你如何使用函数以及函数接收怎样的输入。
自动变量 $_
或 $PSItem
包含管道中的当前对象,以便在 process
块中使用。
$input
自动变量包含一个仅可用于函数和脚本块的枚举器。
有关详细信息,请参阅 about_Automatic_Variables。
- 如果在没有管道输入的情况下调用函数,PowerShell 将只执行一
process
次块。 - 在管道中,
process
块为到达函数的每个输入对象都执行一次。 - 如果到达函数的管道输入为空,则
process
块不会执行。-
begin
end
和clean
块仍在执行。
-
重要
如果函数参数接受管道输入,并且 process
未定义块,则逐条记录处理失败。 在这种情况下,函数仅执行一次,而不考虑输入。
end
使用此块可为函数提供可选的一次性后处理。
clean
clean
块是在 PowerShell 7.3 中添加的。
clean
块是一种方便用户清理跨 begin
、process
和 end
块的资源的方法。 它在语义上类似于 finally
块,该块涵盖脚本函数或脚本 cmdlet 的所有其他命名块。 对于以下场景,将强制执行资源清理:
- 当管道执行完成而不终止错误时
- 管道执行因终止错误而中断
- 截断管道时,例如:
Select-Object -First
- 当管道被 Ctrl+c 停止或
StopProcessing()
清理块将放弃写入 成功 流的任何输出。
注意
添加 clean
块属于一项中断性变更。 由于 clean
是作为关键字分析的,因此它可以阻止用户直接调用名为 clean
的命令作为脚本块中的第一个语句。 然而,这不太可能是个问题。 你仍然可以使用调用运算符 (& clean
) 调用命令。
简单函数
函数不一定要复杂才有用。 最简单的函数采用以下格式:
function <function-name> { statements }
例如,以下函数使用“以管理员身份运行”选项启动 PowerShell。
function Start-PSAdmin { Start-Process PowerShell -Verb RunAs }
若要运行函数,请键入:Start-PSAdmin
若要向函数添加语句,请在单独的行中键入每个语句,或使用分号 (;
) 分隔这些语句。
例如,以下函数查找当前用户目录中在开始日期之后更改的所有 .jpg
文件。
function Get-NewPicture {
$start = Get-Date -Month 1 -Day 1 -Year 2010
$allPics = Get-ChildItem -Path $Env:USERPROFILE\*.jpg -Recurse
$allPics | Where-Object {$_.LastWriteTime -gt $Start}
}
可以创建一个包含实用小型函数的工具箱。 如本文稍后 about_Profiles 中所述,将这些函数添加到 PowerShell 配置文件。
函数名称
可以将任何名称分配给函数。 但是,对于与他人共享的函数,应遵循标准的 PowerShell 命名规则。
- 名称应包含谓词对,其中谓词标识函数执行的作,名词标识 cmdlet 对其执行作的项。
- 名称应对所有 PowerShell 命令使用已批准的谓词。 使用批准的谓词可为用户创建一致性。
有关标准 PowerShell 谓词的详细信息,请参阅批准的谓词。
具有参数的函数
可以结合参数使用函数,包括命名参数、位置参数、开关参数和动态参数。 有关函数中动态参数的详细信息,请参阅 about_Functions_Advanced_Parameters。
命名参数
可以定义任意数量的命名参数。 可以包括命名参数的默认值,如本文后面部分所述。
可以使用 param
关键字在大括号内定义参数,如以下示例语法所示:
function <name> {
param ([type]$Parameter1 [,[type]$Parameter2])
<statement list>
}
还可以在大括号之外定义参数而不使用 param
关键字,如以下示例语法所示:
function <name> [([type]$Parameter1[,[type]$Parameter2])] {
<statement list>
}
例如,以下函数使用替代语法来定义两个参数:
function Add-Numbers([int]$One, [int]$Two) {
$One + $Two
}
虽然首选第一种方法,但这两种方法之间没有区别。
运行该函数时,为参数提供的值将赋给包含参数名称的变量。 该变量的值可以在函数中使用。
以下示例是一个名为 Get-SmallFiles
的函数。 该函数附带 $Size
参数。 该函数显示所有小于 $Size
参数值的文件,并且不包括目录:
function Get-SmallFiles {
param ($Size)
Get-ChildItem $HOME | Where-Object {
$_.Length -lt $Size -and !$_.PSIsContainer
}
}
在该函数中,可以使用 $Size
变量,该变量是为参数定义的名称。
若要使用此函数,请键入以下命令:
Get-SmallFiles -Size 50
还可以为没有参数名称的命名参数输入值。 例如,以下命令提供与命名 Size 参数的命令相同的结果:
Get-SmallFiles 50
若要定义参数的默认值,请在参数名称后面键入等号和值,如 Get-SmallFiles
示例中的以下变体所示:
function Get-SmallFiles ($Size = 100) {
Get-ChildItem $HOME | Where-Object {
$_.Length -lt $Size -and !$_.PSIsContainer
}
}
如果键入 Get-SmallFiles
而不指定值,则函数会将 100 赋给 $size
。 如果提供值,该函数将使用该值。
或者,可以通过将 PSDefaultValue 属性添加到参数描述中并指定 PSDefaultValue 的 Help 属性来提供描述参数默认值的简短帮助字符串。 若要提供描述 函数中 Get-SmallFiles
参数的默认值 (100) 的帮助字符串,请添加 PSDefaultValue 属性,如以下示例所示。
function Get-SmallFiles {
param (
[PSDefaultValue(Help = '100')]
$Size = 100
)
Get-ChildItem $HOME | Where-Object {
$_.Length -lt $Size -and !$_.PSIsContainer
}
}
有关 PSDefaultValue 属性类的详细信息,请参阅 PSDefaultValue 属性成员。
位置参数
位置参数是没有参数名称的参数。 PowerShell 使用参数值顺序将每个参数值与函数中的参数关联。
使用位置参数时,请在函数名称后面键入一个或多个值。 位置参数值赋给 $args
数组变量。
函数名称后面的值赋给 $args
数组中的第一个位置 $args[0]
。
以下 Get-Extension
函数将 .txt
文件扩展名添加到提供的文件名:
function Get-Extension {
$name = $args[0] + ".txt"
$name
}
Get-Extension myTextFile
myTextFile.txt
交换机参数
开关参数是不需要值的参数。 应该键入函数名称,后跟开关参数的名称。
若要定义开关参数,请在参数名称之前指定类型 [switch]
,如以下示例所示:
function Switch-Item {
param ([switch]$On)
if ($On) { "Switch on" }
else { "Switch off" }
}
在函数名称后键入 On
开关参数时,该函数将显示 Switch on
。 如果没有开关参数,它将显示 Switch off
。
Switch-Item -On
Switch on
Switch-Item
Switch off
还可以在运行函数时将布尔值赋给开关,如以下示例所示:
Switch-Item -On:$true
Switch on
Switch-Item -On:$false
Switch off
使用 Splatting 传递参数值
可以使用散列传递来表示命令的参数。 此功能是在 Windows PowerShell 3.0 中引入的。
在会话中调用命令的函数中使用此技术。 无需声明或枚举命令参数,也无需在命令参数更改时更改函数。
以下示例函数调用 Get-Command
cmdlet。 该命令使用 @args
来表示 Get-Command
的参数。
function Get-MyCommand { Get-Command @args }
调用 Get-Command
函数时,可以使用 Get-MyCommand
的所有参数。 参数和参数值使用 @args
传递给命令。
Get-MyCommand -Name Get-ChildItem
CommandType Name ModuleName
----------- ---- ----------
Cmdlet Get-ChildItem Microsoft.PowerShell.Management
@args
功能使用 $args
自动参数,该参数表示未声明的 cmdlet 参数和剩余参数的值。
有关详细信息,请参阅 about_Splatting。
将对象管道到函数
任何函数都可以从管道获取输入。 可以使用 begin
、process
、end
和 clean
关键字来控制函数如何处理来自管道的输入。 以下示例语法显示了这些关键字:
process
语句列表针对管道中的每个对象运行一次。
当 process
块运行时,每个管道对象都被分配给 $_
自动变量,一次分配一个管道对象。
以下函数使用 process
关键字。 该函数显示管道中的值:
function Get-Pipeline
{
process {"The value is: $_"}
}
1, 2, 4 | Get-Pipeline
The value is: 1
The value is: 2
The value is: 4
如果想要一个可以从参数获取管道输入或输入的函数,则 process
块需要处理这两种情况。 例如:
function Get-SumOfNumbers {
param (
[int[]]$Numbers
)
begin { $retValue = 0 }
process {
if ($null -ne $Numbers) {
foreach ($n in $Numbers) {
$retValue += $n
}
} else {
$retValue += $_
}
}
end { $retValue }
}
PS> 1, 2, 3, 4 | Get-SumOfNumbers
10
PS> Get-SumOfNumbers 1, 2, 3, 4
10
在管道中使用函数时,传递给函数的对象将分配给 $input
自动变量。 该函数在任何对象来自管道之前,使用 begin
脚本块运行语句。 当管道中没有更多对象时, end
该函数将运行包含脚本块的语句。
以下示例显示了$input
用于 int 和end
脚本块的begin
自动变量。
function Get-PipelineBeginEnd {
begin { "Begin: The input is $input" }
end { "End: The input is $input" }
}
使用管道输入运行函数时,会显示以下结果:
1, 2, 4 | Get-PipelineBeginEnd
Begin: The input is
End: The input is 1 2 4
当 begin
语句运行时,该函数没有来自管道的输入。
end
语句在函数获得值后运行。
如果函数有 process
关键字,$input
中的每个对象都会从 $input
中移除并分配给 $_
。 以下示例包含 process
语句列表:
function Get-PipelineInput
{
process {"Processing: $_ " }
end {"End: The input is: $input" }
}
在此示例中,传递给函数的每个对象将发送到 process
语句列表。
process
语句在每个对象上运行,一次运行一个对象。 当函数到达 $input
关键字时,end
自动变量为空。
1, 2, 4 | Get-PipelineInput
Processing: 1
Processing: 2
Processing: 4
End: The input is:
有关详细信息,请参阅使用枚举器
PowerShell 7.3 添加了 clean
块。
clean
块是一种方便用户清理 begin
、process
和 end
块中创建和使用的资源的方法。 它在语义上类似于 finally
块,该块涵盖脚本函数或脚本 cmdlet 的所有其他命名块。 对于以下场景,将强制执行资源清理:
- 管道执行正常完成,没有终止错误
- 管道执行因终止错误而中断
- 截断管道时,例如:
Select-Object -First
- 当管道被 Ctrl+C 停止或
StopProcessing()
注意
添加 clean
块属于一项中断性变更。 由于 clean
是作为关键字分析的,因此它可以阻止用户直接调用名为 clean
的命令作为脚本块中的第一个语句。 然而,这不太可能是个问题。 仍可以使用调用运算符 (& clean
) 调用该命令。
函数范围
在其中创建函数的作用域中存在一个函数。
如果函数是脚本的一部分,该函数可用于该脚本中的语句。 默认情况下,脚本中的函数在该脚本之外不可用。
可以指定函数的作用域。 例如,该函数将添加到以下示例中的全局作用域:
function Global:Get-DependentSvs {
Get-Service | Where-Object {$_.DependentServices}
}
当函数位于全局作用域内时,可以在脚本、函数和命令行中使用函数。
函数会创建新的作用域。 在函数中创建的项(如变量)仅存在于函数作用域中。
有关详细信息,请参阅 about_Scopes。
使用 Function:
驱动器查找和管理函数
PowerShell 中的所有函数和筛选器都自动存储在 Function:
驱动器中。 此驱动器由 PowerShell Function 提供程序公开。
引用 Function:
驱动器时,请在 Function 后键入冒号,就像引用计算机的 C
或 D
驱动器那样。
以下命令显示 PowerShell 当前会话中的所有函数:
Get-ChildItem Function:
函数中的命令作为脚本块存储在函数的定义属性中。 例如,若要在 PowerShell 附带的 Help 函数中显示命令,请键入:
(Get-ChildItem Function:help).Definition
还可以使用以下语法。
$Function:help
有关详细信息,请参阅 about_Function_Provider。
在新会话中重复使用函数
在 PowerShell 命令提示符下键入函数后,该函数将成为当前会话的一部分。 该函数在会话结束之前可用。
若要在所有 PowerShell 会话中使用函数,请将该函数添加到 PowerShell 配置文件。 有关配置文件的详细信息,请参阅 about_Profiles。
还可以将函数保存在 PowerShell 脚本文件中。 在文本文件中键入函数,然后使用 .ps1
文件扩展名保存该文件。
为函数创建帮助
该 Get-Help
cmdlet 获取函数、cmdlet、提供程序和脚本的帮助。
若要获取有关某个函数的帮助,请键入 Get-Help
,后跟函数名称。
例如,若要获取有关 Get-MyDisks
函数的帮助,请键入:
Get-Help Get-MyDisks
可以使用以下两种方法之一为函数编写帮助:
基于注释的函数帮助
在注释中使用特殊关键字创建帮助。 若要为函数创建基于注释的帮助,必须将注释放在函数的开头、结尾或正文中。 有关基于注释的帮助的详细信息,请参阅 about_Comment_Based_Help。
基于 XML 的函数帮助
如果需要将帮助内容本地化为多种语言,则需要基于 XML 的帮助。 若要将函数与基于 XML 的帮助文件相关联,请使用基于注释的
.EXTERNALHELP
帮助关键字。 如果没有此关键字,Get-Help
则找不到函数帮助文件,并且仅返回自动生成的帮助。有关
.EXTERNALHELP
关键字的详细信息,请参阅 about_Comment_Based_Help。 有关基于 XML 的帮助的详细信息,请参阅如何编写 Cmdlet 帮助。
另请参阅
- about_Automatic_Variables
- about_Comment_Based_Help
- about_Function_Provider
- about_Functions_Advanced
- about_Functions_Advanced_Methods
- about_Functions_Advanced_Parameters
- about_Functions_CmdletBindingAttribute
- about_Functions_OutputTypeAttribute
- about_Parameters
- about_Profiles
- about_Scopes
- about_Script_Blocks