[玩转系统] 实用 PowerShell:函数和参数化
作者:精品下载站 日期:2024-12-14 04:13:17 浏览:11 分类:玩电脑
实用 PowerShell:函数和参数化
在我的上一篇文章中,我介绍了实用PowerShell系列。在处理 PowerShell 脚本时,可能会出现一组指令重复脚本中其他位置的代码的情况。您也可能希望将其他地方的代码合并到您的脚本中,以便您可以轻松调用这些代码。
这一描述可能会让您想起 cmdlet,它们具有名称以及(可选)一个或多个用于控制其操作的参数。但是,如果您希望代码具有相同的功能,例如具有高可重用性的代码,该怎么办?欢迎来到 PowerShell 函数的世界。在深入讨论之前,让我们先明确一下我们正在谈论的内容。
脚本
脚本是一个扩展名为 .ps1 的文本文件,其中包含 PowerShell 代码。该代码由 cmdlet 和(可选)函数组成。您可以通过多种方式调用脚本:
- 使用&符号(& .\Process-Something.ps1)。该代码在具有其作用域的子会话中运行。这意味着脚本中的任何定义(例如变量或函数)都会在脚本终止时消失。当您运行交互式 PowerShell 脚本时,通常会省略 & 符号,但是当您想要运行变量中指向的代码时,您必须使用此调用方法,例如 & { Get-ChildItem }
确保了解在命令开头和结尾使用 & 符号之间的区别。在末尾使用“&”号指示 PowerShell 在后台作业中运行代码。 - 使用点源(例如 .\Helper-Functions.ps1),代码在当前 PowerShell 会话中运行。这意味着您在脚本中定义的任何变量或函数在会话中都可用。如果这些定义以前存在,它们就会被覆盖。
运行 PowerShell 脚本的能力取决于本地计算机当前的执行策略。这是防止恶意脚本运行的安全措施。来自 Microsoft 的脚本通常经过签名,但从互联网下载的许多社区来源的脚本通常没有签名。您可能需要先运行Set-ExecutionPolicy unrestricted,然后才能运行其他人创建的脚本,前提是公司政策不阻止您修改执行策略。
可重用的代码
函数是合并到脚本中的一组可重用代码。要识别代码,您必须为其命名,并在命令Function之后指定。使用 PowerShell 约定,函数名称应遵循动词-名词命名约定。为了避免任何冲突,您可以为名词添加前缀,例如
Function Get-MyReport {
#reusable code
}
如果您创建重新定义现有命令的函数,则现有定义将在当前会话中被覆盖。代码的输出将返回给调用者,您可以将结果输出到屏幕或将其分配给变量以进行进一步处理。
除了函数本身之外,您还可以为函数中包含的代码定义参数。然后,函数内的代码可以使用通过参数传递的信息来执行其任务,因为参数成为函数代码上下文中可用的变量。
一个例子解释了这个概念。以下是一个虚构函数,用于在指定 MemberCount 参数时获取通讯组的详细信息以及通讯组的成员数量。 MemberCount 属性包含成员数量,或者在未指定 MemberCount 开关时被忽略。
这种函数的基本定义可能如下所示:
Function Get-DistributionGroupInfo( $Identity, $MemberCount)
{
Get-DistributionGroup $Identity | Select-Object Identity,PrimarySmtpAddress, @{n='MemberCount';e={ If( $MemberCount) { (Get-DistributionGroupMember -Identity $_ | Measure-Object).Count } }}}
}
在起草脚本或进行概念验证时,此代码是可以接受的。然而,当稍后使用该函数或者不太熟悉该用例的其他人负责该代码时,问题可能会变得明显。
此功能的问题包括:
- 变量 $Identity 中传递的通讯组可以是未指定的 ($null)。这可能会导致意想不到的副作用,因为当您指定 $null 作为值时,许多 Get cmdlet 会很乐意返回所有对象。采取以下代码:
Function Process-Mailbox( $Id) {
Get-Mailbox -Identity $Id | Set-Mailbox -HiddenFromAddressListEnabled $True
}
你能猜一下如果不传递ID参数或者ID为空会发生什么吗?所有邮箱都会被返回,Set-Mailbox将很高兴地隐藏地址列表中的所有邮箱。这可能不是您想要的。
- 在上面的示例中,Identity 和 Members 可以是任何内容;它们不需要分别是通讯组或交换机。您可以添加代码来检查 $Identity 是否为通讯组以及 Members 是否为布尔值($true 或 $false),但这需要额外的代码。如果必须处理多个参数,代码可能会变得相当复杂。
幸运的是,PowerShell 有多种机制可以帮助您定义参数要求。让我们看一下以下高级函数的示例:
Function Get-DistributionGroupInfo {
[CmdletBinding()]
Param(
[Parameter(Position= 0, Mandatory= $true, ValueFromPipeline= $true, ValueFromPipelineByPropertyName=$true, HelpMessage= 'Please provide a Distribution Group')]
[String]$Identity,
[Parameter(HelpMessage= 'Output member count')]
[Switch]$MemberCount
)
Process {
Write-Verbose ('Fetching Distribution Group {0}' -f $Identity)
Get-DistributionGroup $Identity | Select-Object Identity,PrimarySmtpAddress, @{n='MemberCount';e={ If( $MemberCount) { (Get-DistributionGroupMember -Identity $_ | Measure-Object).Count } }}
}
}
此高级功能包含以下增强功能:
- 第一个参数定义之前的[CmdletBinding()]告诉PowerShell该函数支持通用参数。常见参数的示例包括 Verbose 和Confirm。然后,您可以在函数中包含代码来支持这一点。例如,如果您传递 -Verbose 并且您的函数包含 Write-Verbose 命令,则将显示详细输出。当您省略 -Verbose 时,不会显示输出。
- 第一个参数规范中的 Position=0 指示 PowerShell 将传递给 Get-DistributionGroupInfo 的第一个未命名参数视为身份。因此,以下两个命令是相同的:
Get-DistributionGroupInfo -Identity 'DG-X'
Get-DistributionGroupInfo 'DG-X'
其他未命名参数可以指定为 Position=1 等
- Mandatory=$true 告诉 PowerShell 该参数是强制性的。当用户在运行脚本时省略身份时,PowerShell 将要求提供身份。通过将 Mandatory 设置为 $false 或省略条件,参数可以是可选的。
- 我们希望在管道中调用函数时能够传递 Identity。您可以通过指定 ValueFromPipeline 来启用此参数的管道使用。您可以通过指定ValueFromPipelineByPropertyName来使用传递对象的属性。在管道中调用该函数可以如下所示:
Get-DistributionGroup -Identity 'DG-X' | Get-DistributionGroupInfo
- 当您省略强制参数时,HelpMessage 定义帮助信息。当 PowerShell 要求您输入时输入 !? 时会显示此帮助信息,并且当您使用以下命令时也会显示此帮助信息:
Get-Help Get-DistributionGroupInfo -Full
我不知道有谁使用!?,但如果你愿意的话你可以。
- 指定约束后,您可以定义参数本身。您可以通过为其指定名称(在本例中为身份)来完成此操作。该名称包含调用函数时作为参数传递的值,使其在其范围内可用。您可以选择定义参数将接受的对象类型。在本例中,我们指定 [String],它等于 [System.String],但 PowerShell 有一些用于内置类型的类型加速器(短别名)。
严格输入参数的好处是,当您传递不同类型的对象(例如整数)时,PowerShell 将抛出错误,指出传递的内容和预期的内容。例如,下面的基本函数接受单个参数A,该参数需要是整数(int)类型。传递数字与传递字符串将产生以下输出:
Function Test {
param(
[int]$A
)
Write-Output ($A)
}
❯ test -A 123
123
❯ test -A 'string'
Test: Cannot process argument transformation on parameter 'A'. Cannot convert value "string" to type "System.Int32". Error: "The input string 'string' was not in a correct format."
我建议尽可能使用严格的参数类型。键入有助于解决使用问题,还有助于记录代码,如下所述。需要考虑的一件事是,值可能会通过解释进行转换。例如,如果您传递参数值 123,并且需要一个字符串,PowerShell 会很乐意将其转换为字符串表示形式“123”。
- 第二个参数(注意 Identity 后面的逗号)是一个名为 MemberCount 的 Switch。由于该参数不是强制性的并且不需要使用管道,因此定义中没有指定这些。开关的好处是您只需提及即可使用它们,例如-会员计数;会将 MemberCount 变量设置为 $true。如果不这样做,它将是 $false。您不能使用 -MemberCount $true,因为 PowerShell 会将 $true 解释为下一个参数,因为 MemberCount 是一个开关。如果需要,例如当 $true 或 $false 存储在变量中时,您可以通过指定 : 使用变量来设置它,例如-会员计数:$false。当避免必须确认某些命令时,例如,您可能已经在使用此语法。设置邮箱…-确认:$False
- 通过将执行实际任务的代码放入 Process 脚本块 {} 中,我们使该函数适用于通过管道传递的对象。如果我们省略这一点并保持代码不变,它将不支持管道操作,并且代码只会针对通过管道接收的最后一个对象执行一次。请注意,如果需要,管道中的当前对象可通过 Process 脚本块中的自动变量 $_ 获得。
当我们将此函数的代码放入脚本文件(例如 MyDemo.ps1)中时,它就可以在我们的 PowerShell 会话中使用。为了实现这一点,我们需要点源它以在我们的会话中定义它。然后,我们可以调用它(前提是已加载并连接 Exchange Online Management Shell),并通过调用 Get-Help(其中还包括文档)检查其定义。
PS❯ . .\MyDemo.ps1
PS❯ Get-DistributionGroupInfo -Identity MyDG -MemberCount -Verbose
VERBOSE: Fetching Distribution Group MyDG
Identity PrimarySmtpAddress MemberCount
-------- ------------------ -----------
MyDG [email protected] 2
PS❯ Get-DistributionGroup | Get-DistributionGroupInfo -MemberCount
Identity PrimarySmtpAddress MemberCount
-------- ------------------ -----------
MyDG [email protected] 2
OtherDG [email protected] 8
PS❯ Get-Help Get-DistributionGroupInfo -Full
NAME
Get-DistributionGroupInfo
SYNTAX
Get-DistributionGroupInfo [-Identity] <string> [-MemberCount] [<CommonParameters>]
PARAMETERS
-Identity <string>
Please provide a Distribution Group.
Required? True
Position? 0
Accept pipeline input? true (ByValue)
Parameter set name (All)
Aliases None
Dynamic? False
Accept wildcard characters? False
-MemberCount
Output member count
Required? False
Position? Named
Accept pipeline input? False
Parameter set name (All)
Aliases None
Dynamic? False
Accept wildcard characters? False
<CommonParameters>
This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer, PipelineVariable, and OutVariable. For more information, see
about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216).
开始、过程、结束
示例函数支持通过管道传递对象。 Process 脚本块适用于通过管道传递的每个项目。但是,如果我们想在处理这些对象之前和之后执行一些内务处理怎么办?例如,我们想在处理所有对象之前初始化一些变量。
为此,我们可以在 Process 块之前和之后添加 Begin 和 End 脚本块。
Begin {
# Initialize
$Items=0
}
Process {
# Do Something
$Items++
}
End {
# Cleanup
Write-Host ('We processed {0} object(s)' -f $Items)
}
一个示例是,当您想要计算已处理的对象数量时,因为通过管道传递的对象数量预先未知。另一个例子是 Sort-Object 命令,它只能在所有对象都传递给它时才对对象进行排序。
该脚本可能有一个管道函数,其中包含一个 Begin 脚本块来初始化数据集,Process 将所有项目添加到其中,最后End > 执行排序。请注意,Begin 和 End 块是可选的。另外,如果没有传递任何对象,则跳过 Process 块,但 Begin 和 End 将在定义时执行。
脚本参数
我们讨论了如何为函数定义参数,但是如何为脚本定义参数呢?答案是,为脚本定义参数的方式与为函数定义参数的方式类似,但发生在脚本级别。实际上,这意味着将定义放在脚本的开头。例如,以下是脚本的前几行:
[CmdletBinding()]
Param(
[parameter( Mandatory= $true)
[ValidateScript({ Test-Path -Path $_ -PathType Leaf})]
[String]$CSVFile,
[ValidateSet(',', ';')]
[string]$Delimiter=',',
[System.Security.SecureString]$Password
)
Function X {
# …
}
#etc.
当未给出值时,要求为 $CSVFile 提供一个值(强制= $true)。您会注意到 ValidateScript 行作为 CSVFile 参数定义的一部分。指定参数时,您可以让 PowerShell 针对提供的值执行某些验证。一些可能的测试是:
- ValidateScript 用于执行需要返回 $true 才能接受参数值的脚本块。在示例中,我们使用指定自动变量 $_ (实际文件名)的 Test-Path 检查文件名是否有效。
- ValidateSet 根据一组预定义值测试该值。在示例中,我们使用 ValidateSet 仅允许 $Delimiter 参数使用逗号或分号。
- 其他一些选项包括 ValidateRange、ValidateCount、ValidatePattern 和 ValidateLength a.o。有关参数验证的更多信息可以在此处找到。
让 PowerShell 为您服务
在一篇文章中讨论 PowerShell 中的函数和参数是雄心勃勃的。我没有涉及其他主题,例如命令集和动态参数。这些可能是另一篇文章的主题。
我希望我鼓励您编写可重用的代码,不仅可以利用脚本和函数,还可以正确定义代码。尽可能让 PowerShell 为您服务,让它处理参数验证并检查其他约束(例如类型)。这使您能够专注于任务,同时降低代码的复杂性并提高可读性。
如果您有疑问或意见,请随时在评论中联系。如果没有,请等到下一篇文章,我将讨论流量控制。
猜你还喜欢
- 03-30 [玩转系统] 如何用批处理实现关机,注销,重启和锁定计算机
- 02-14 [系统故障] Win10下报错:该文件没有与之关联的应用来执行该操作
- 01-07 [系统问题] Win10--解决锁屏后会断网的问题
- 01-02 [系统技巧] Windows系统如何关闭防火墙保姆式教程,超详细
- 12-15 [玩转系统] 如何在 Windows 10 和 11 上允许多个 RDP 会话
- 12-15 [玩转系统] 查找 Exchange/Microsoft 365 中不活动(未使用)的通讯组列表
- 12-15 [玩转系统] 如何在 Windows 上安装远程服务器管理工具 (RSAT)
- 12-15 [玩转系统] 如何在 Windows 上重置组策略设置
- 12-15 [玩转系统] 如何获取计算机上的本地管理员列表?
- 12-15 [玩转系统] 在 Visual Studio Code 中连接到 MS SQL Server 数据库
- 12-15 [玩转系统] 如何降级 Windows Server 版本或许可证
- 12-15 [玩转系统] 如何允许非管理员用户在 Windows 中启动/停止服务
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[影视] 黑道中人 Alto Knights(2025)剧情 犯罪 历史 电影
[古装剧] [七侠五义][全75集][WEB-MP4/76G][国语无字][1080P][焦恩俊经典]
[实用软件] 虚拟手机号 电话 验证码 注册
[电视剧] 安眠书店/你 第五季 You Season 5 (2025) 【全10集】
[电视剧] 棋士(2025) 4K 1080P【全22集】悬疑 犯罪 王宝强 陈明昊
[软件合集] 25年6月5日 精选软件22个
[软件合集] 25年6月4日 精选软件36个
[短剧] 2025年06月04日 精选+付费短剧推荐33部
[短剧] 2025年06月03日 精选+付费短剧推荐25部
[软件合集] 25年6月3日 精选软件44个
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电视剧] 欢乐颂.5部全 (2016-2024)
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[影视] 【稀有资源】香港老片 艺坛照妖镜之96应召名册 (1996)
[剧集] 神经风云(2023)(完结).4K
[剧集] [BT] [TVB] [黑夜彩虹(2003)] [全21集] [粤语中字] [TV-RMVB]
[实用软件] 虚拟手机号 电话 验证码 注册
[资源] B站充电视频合集,包含多位重量级up主,全是大佬真金白银买来的~【99GB】
[影视] 内地绝版高清录像带 [mpg]
[书籍] 古今奇书禁书三教九流资料大合集 猎奇必备珍藏资源PDF版 1.14G
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电影] 美国队长4 4K原盘REMUX 杜比视界 内封简繁英双语字幕 49G
[电影] 死神来了(1-6)大合集!
[软件合集] 25年05月13日 精选软件16个
[精品软件] 25年05月15日 精选软件18个
[绝版资源] 南与北 第1-2季 合集 North and South (1985) /美国/豆瓣: 8.8[1080P][中文字幕]
[软件] 25年05月14日 精选软件57个
[短剧] 2025年05月14日 精选+付费短剧推荐39部
[短剧] 2025年05月15日 精选+付费短剧推荐36部
- 最新评论
-
- 热门tag