编辑说明
重要
Windows PowerShell 语言规范 3.0 于 2012 年 12 月发布,基于 Windows PowerShell 3.0。 此规范不反映 PowerShell 的当前状态。 没有计划更新本文档以反映当前状态。 此处提供了本文档供历史参考。
规范文档可从 Microsoft 下载中心以 Microsoft Word 文档的形式获取,网址为:https://www.microsoft.com/download/details.aspx?id=36389 该 Word 文档已转换为 Microsoft Learn 上的演示文稿。 转换期间,进行了一些编辑更改,以适应 Docs 平台的格式设置。 已更正某些拼写错误和次要错误。
11.1 简介
如 §3.14中所述,模块是一个自包含的可重用单元,允许对 PowerShell 代码进行分区、组织和抽象。 模块可以包含一个或多个 模块成员,这些成员是命令(如 cmdlet 和函数)和项(如变量和别名)。 这些成员的名称可以对模块保密,也可以将它们导出到模块导入的会话中。
有三种不同的 模块类型:,清单、脚本和二进制。 清单模块 是一个文件,其中包含有关模块的信息,并控制该模块使用的某些方面。 脚本模块 是一个 PowerShell 脚本文件,文件扩展名为 .psm1
,而不是 .ps1
。 二进制模块 包含定义 cmdlet 和提供程序的类类型。 与脚本模块不同,二进制模块是用编译语言编写的。 此规范未涵盖二进制模块。
二进制模块是针对 PowerShell 库编译的 .NET 程序集(即 DLL)。
模块可以 嵌套;也就是说,一个模块可以导入另一个模块。 具有关联嵌套模块的模块是 根模块。
创建 PowerShell 会话时,默认情况下不会导入任何模块。
导入模块时,用于查找它们的搜索路径由环境变量 PSModulePath定义。
以下命令行脚本与模块相关:
- Get-Module:标识已导入或可导入的模块
- Import-Module:将一个或多个模块添加到当前会话(请参阅 §11.4)
- Export-ModuleMember:标识要导出的模块成员
- Remove-Module:从当前会话中删除一个或多个模块(请参阅 §11.5)
- 新模块:创建动态模块(请参阅 §11.7)
11.2 编写脚本模块
脚本模块是脚本文件。 请考虑以下脚本模块:
function Convert-CentigradeToFahrenheit ([double]$tempC) {
return ($tempC * (9.0 / 5.0)) + 32.0
}
New-Alias c2f Convert-CentigradeToFahrenheit
function Convert-FahrenheitToCentigrade ([double]$tempF) {
return ($tempF - 32.0) * (5.0 / 9.0)
}
New-Alias f2c Convert-FahrenheitToCentigrade
Export-ModuleMember -Function Convert-CentigradeToFahrenheit
Export-ModuleMember -Function Convert-FahrenheitToCentigrade
Export-ModuleMember -Alias c2f, f2c
此模块包含两个函数,每个函数都有一个别名。 默认情况下,导出所有函数名,且只导出函数名。 但是,一旦使用 cmdlet Export-ModuleMember
导出任何内容,则只会导出显式导出的内容。 可以通过一个调用或多次调用此 cmdlet 来导出一系列命令和项;这些调用在当前会话中是累积的。
11.3 安装脚本模块
脚本模块在脚本文件中定义,模块可以存储在任何目录中。 当与模块相关的 cmdlet 查找名称不包含完全限定路径的模块时,环境变量 PSModulePath 指向一组要搜索的目录。 可以提供其他查找路径;例如
$Env:PSModulepath = $Env:PSModulepath + ";<additional-path>"
添加的任何其他路径仅影响当前会话。
或者,在导入模块时可以指定完全限定的路径。
11.4 导入脚本模块
在使用模块中的资源之前,必须使用 cmdlet Import-Module
将该模块导入到当前会话中。 Import-Module
可以限制实际导入的资源。
导入模块后,将执行其脚本文件。 可以通过在脚本文件中定义一个或多个参数,并通过 Import-Module
ArgumentList 参数传入相应的参数来配置该过程。
请考虑以下脚本,该脚本使用 §11.2中定义的这些函数和别名:
Import-Module
“E:\Scripts\Modules\PSTest_Temperature”-Verbose
"0 degrees C is " + (Convert-CentigradeToFahrenheit 0) + " degrees F"
"100 degrees C is " + (c2f 100) + " degrees F"
"32 degrees F is " + (Convert-FahrenheitToCentigrade 32) + " degrees C"
"212 degrees F is " + (f2c 212) + " degrees C"
当模块中的命令或项与会话中的命令或项具有相同的名称时,导入模块会导致名称冲突。 名称冲突导致名称被隐藏或替换。 Import-Module
的 Prefix 参数可用于避免命名冲突。 此外,别名、Cmdlet、函数,以及 变量 参数可以限制要导入的命令的选择,从而减少名称冲突的可能性。
即使命令被隐藏,也可以通过使用它所在模块的名称来限定它的名称来运行它。 例如,& M\F 100
在模块 M中调用函数 F,并传递参数 100。
当会话包含具有相同名称的相同类型的命令(例如具有相同名称的两个 cmdlet)时,默认情况下,它运行最近添加的命令。
请参阅 §3.5.6,了解与模块相关的范围。
11.5 删除脚本模块
可以通过 cmdlet Remove-Module
从会话中删除一个或多个模块。
删除模块不会卸载该模块。
在脚本模块中,可以指定在删除该模块之前要执行的代码,如下所示:
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { *on-removal-code* }
11.6 模块清单
如 §11.1中所述,清单模块是包含有关模块的信息的文件,并控制该模块使用的某些方面。
模块不需要相应的清单,但如果具有相应的清单,该清单的名称与它描述的模块相同,但具有 .psd1
文件扩展名。
清单包含 PowerShell 脚本的有限子集,该脚本返回包含一组键的哈希表。 这些键及其值指定了该模块的清单元素。 也就是说,它们描述模块的内容和属性,定义任何先决条件,并确定组件的处理方式。
实质上,清单是数据文件;但是,它可以包含对数据类型、if 语句和算术和比较运算符的引用。 (不允许赋值、函数定义和循环。清单还具有对环境变量的读取访问权限,并且它可以包含对 cmdlet Join-Path
的调用,因此可以构造路径。
注意
编辑器备注:原始文档包含模块清单文件中允许的键列表。 该列表已过时且不完整。 有关模块清单中密钥的完整列表,请参阅 New-ModuleManifest。
所需的唯一密钥是 ModuleVersion。
下面是简单清单的示例:
@{
ModuleVersion = '1.0'
Author = 'John Doe'
RequiredModules = @()
FunctionsToExport = 'Set*','Get*','Process*'
}
密钥 GUID 具有 string
值。 这为模块指定了一个全局唯一标识符(GUID)。 GUID 可用于区分具有相同名称的模块。 若要创建新的 GUID,请调用方法 [guid]::NewGuid()
。
11.7 动态模块
动态模块 是在运行时由 cmdlet New-Module
在内存中创建的模块,不会从磁盘加载。 请考虑以下示例:
$sb = {
function Convert-CentigradeToFahrenheit ([double]$tempC) {
return ($tempC * (9.0 / 5.0)) + 32.0
}
New-Alias c2f Convert-CentigradeToFahrenheit
function Convert-FahrenheitToCentigrade ([double]$tempF) {
return ($tempF - 32.0) * (5.0 / 9.0)
}
New-Alias f2c Convert-FahrenheitToCentigrade
Export-ModuleMember -Function Convert-CentigradeToFahrenheit
Export-ModuleMember -Function Convert-FahrenheitToCentigrade
Export-ModuleMember -Alias c2f, f2c
}
New-Module -Name MyDynMod -ScriptBlock $sb
Convert-CentigradeToFahrenheit 100
c2f 100
脚本块 $sb
定义模块的内容,在本例中为这些函数定义两个函数和两个别名。 与磁盘上的模块一样,默认情况下仅导出函数,因此存在 Export-ModuleMember
cmdlet 调用来导出函数和别名。
运行 New-Module
后,导出的四个名称可用于会话,如对 Convert-CentigradeToFahrenheit
和 c2f 的调用所示。
与所有模块一样,动态模块中的成员在全局作用域的子级私有模块作用域内运行。 Get-Module
无法获取动态模块,但 Get-Command
可以获取导出的成员。
若要使动态模块可用于 Get-Module
,请通过管道将 New-Module
命令传递给 Import-Module
,或者通过管道将 New-Module
返回的模块对象传递给 Import-Module
。 此操作将动态模块添加到 Get-Module
列表中,但它不会将模块保存到磁盘或使其持久化。
11.8 闭包
动态模块可用于创建闭包,即一个具有附加数据的函数。 请考虑以下示例:
function Get-NextID ([int]$startValue = 1) {
$nextID = $startValue
{
($script:nextID++)
}.GetNewClosure()
}
$v1 = Get-NextID # get a scriptblock with $startValue of 0
& $v1 # invoke Get-NextID getting back 1
& $v1 # invoke Get-NextID getting back 2
$v2 = Get-NextID 100 # get a scriptblock with $startValue of 100
& $v2 # invoke Get-NextID getting back 100
& $v2 # invoke Get-NextID getting back 101
此处的意向是,Get-NextID
返回可指定起始值的序列中的下一个 ID。 但是,必须支持多个序列,每个序列都有自己的 $startValue
和 $nextID
上下文。 这是通过调用方法 [scriptblock]::GetNewClosure
(§4.3.7)来实现的。
每次通过 GetNewClosure
创建新的闭包时,都会生成一个新的动态模块,调用者的作用域中的变量(在本例中,是包含增量的脚本块)将被复制到这个新的模块中。 为了确保在父函数内(但在脚本块外)定义的 nextId 递增,需要显式的 script: scope 前缀。
当然,脚本代码块不必是一个命名函数;例如:
$v3 = & { # get a scriptblock with $startValue of 200
param ([int]$startValue = 1)
$nextID = $startValue
{
($script:nextID++)
}.GetNewClosure()
} 200
& $v3 # invoke script getting back 200
& $v3 # invoke script getting back 201