当我开始学习 PowerShell 时,我最初依赖于图形用户界面(GUI),这些任务对于简单 PowerShell 命令来说似乎过于复杂。 但是,当我继续学习时,我改进了我的技能,从基本的单行程序迁移到创建脚本、函数和模块。 请务必记住,在线高级示例让人不知所措的感觉是正常的。 没有人在 PowerShell 中以专家身份开始;我们都从初学者开始。
对于主要对管理任务使用 GUI 的用户,请在管理工作站上安装管理工具以远程管理服务器。 无论你的服务器是使用 GUI 还是服务器核心 OS 安装,此方法都是有益的。 这是一种熟悉远程服务器管理的实际方法,用于准备使用 PowerShell 执行管理任务。
与前面的章节一样,请在实验室环境中尝试这些概念。
单行式命令
PowerShell 单行式命令是一个连续的管道。 一种常见的误解是,一行物理命令被认为是 PowerShell 单行式命令,但这并不总是正确的。
例如,考虑以下示例:该命令在多个物理行上扩展,但它是 PowerShell 单行式命令,因为它形成了一个连续的管道。 建议在管道符号处断行很长的单行式命令,这是 PowerShell 中的自然断点,以增强可读性和清晰度。 这种对换行符的策略性使用增强了可读性,同时不影响管道的顺畅进行。
Get-Service |
Where-Object CanPauseAndContinue -EQ $true |
Select-Object -Property *
Name : LanmanWorkstation
RequiredServices : {NSI, MRxSmb20, Bowser}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Workstation
DependentServices : {SessionEnv, Netlogon}
MachineName : .
ServiceName : LanmanWorkstation
ServicesDependedOn : {NSI, MRxSmb20, Bowser}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Automatic
Site :
Container :
Name : Netlogon
RequiredServices : {LanmanWorkstation}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Netlogon
DependentServices : {}
MachineName : .
ServiceName : Netlogon
ServicesDependedOn : {LanmanWorkstation}
ServiceHandle :
Status : Running
ServiceType : Win32ShareProcess
StartType : Automatic
Site :
Container :
Name : vmicheartbeat
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Heartbeat Service
DependentServices : {}
MachineName : .
ServiceName : vmicheartbeat
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmickvpexchange
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Data Exchange Service
DependentServices : {}
MachineName : .
ServiceName : vmickvpexchange
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmicrdv
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Remote Desktop Virtualization Service
DependentServices : {}
MachineName : .
ServiceName : vmicrdv
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmicshutdown
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Guest Shutdown Service
DependentServices : {}
MachineName : .
ServiceName : vmicshutdown
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmicvss
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Volume Shadow Copy Requestor
DependentServices : {}
MachineName : .
ServiceName : vmicvss
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : webthreatdefsvc
RequiredServices : {RpcSs, wtd}
CanPauseAndContinue : True
CanShutdown : True
CanStop : True
DisplayName : Web Threat Defense Service
DependentServices : {}
MachineName : .
ServiceName : webthreatdefsvc
ServicesDependedOn : {RpcSs, wtd}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : webthreatdefusersvc_644de
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : True
CanStop : True
DisplayName : Web Threat Defense User Service_644de
DependentServices : {}
MachineName : .
ServiceName : webthreatdefusersvc_644de
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : 240
StartType : Automatic
Site :
Container :
Name : Winmgmt
RequiredServices : {RPCSS}
CanPauseAndContinue : True
CanShutdown : True
CanStop : True
DisplayName : Windows Management Instrumentation
DependentServices : {}
MachineName : .
ServiceName : Winmgmt
ServicesDependedOn : {RPCSS}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Automatic
Site :
Container :
自然换行符可以出现在常用的字符中,包括逗号(,
)和左括号([
)、大括号({
)和括号((
)。 其他不太常见的符号包括分号(;
)、等号(=
),以及单引号和双引号的起始部分('
,"
)。
将反引号 (`
) 或重音字符用作行继续符是有争议的。 最好尽可能避免它。 在自然换行符之后使用反引号是一个常见错误。 这种冗余是不必要的,并且可能会混乱代码。
以下示例中的命令从 PowerShell 控制台正确执行。 但是,尝试在 PowerShell 集成脚本环境(ISE)的控制台窗格中运行它们会导致错误。 发生此差异的原因是,与 PowerShell 控制台不同,ISE 的控制台窗格不会自动预测命令的延续到下一行。 为了防止此问题,请在 ISE 的控制台窗格中按 Shift+Enter,而不是当需要跨多行扩展命令时按 Enter。 此组合键向 ISE 发出信号,指示命令在以下行中继续,从而阻止导致错误的执行。
Get-Service -Name w32time |
Select-Object -Property *
Name : w32time
RequiredServices : {}
CanPauseAndContinue : False
CanShutdown : True
CanStop : True
DisplayName : Windows Time
DependentServices : {}
MachineName : .
ServiceName : w32time
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
下一个示例不符合 PowerShell 单行式命令的条件,因为它不是一个连续的管道。 相反,它是放在一行上的两个单独的命令,用分号分隔。 此分号表示一个命令的末尾和另一个命令的开头。
$Service = 'w32time'; Get-Service -Name $Service
Status Name DisplayName
------ ---- -----------
Running w32time Windows Time
许多编程和脚本语言都需要每行末尾的分号。 但是,在 PowerShell 中,行尾的分号是不必要的,不建议这样做。 应该避免使用它们,以使代码更简洁且更易于阅读。
左侧筛选器
本章演示如何筛选各种命令的结果。
PowerShell 中的最佳做法是尽早在管道中筛选结果。 实现这一点需要在初始命令上使用参数应用筛选器,通常在管道开头。 这通常称作左侧筛选器。
为了说明此概念,请考虑以下示例:使用 的 Get-Service
参数筛选管道开头的结果,只返回 Windows 时间服务的详细信息。 此方法演示了高效的数据检索,确保仅返回必要和相关信息。
Get-Service -Name w32time
Status Name DisplayName
------ ---- -----------
Running w32time Windows Time
通常可以看到 PowerShell 命令被传输到 Where-Object
cmdlet 以筛选其结果的在线示例。 如果管道中的早期命令具有执行筛选的参数,则此方法效率低下。
Get-Service | Where-Object Name -EQ w32time
Status Name DisplayName
------ ---- -----------
Running W32Time Windows Time
第一个示例演示了直接在源代码处进行筛选,返回专门针对 Windows 时间服务的结果。 相反,第二个示例检索所有服务,然后使用另一个命令筛选结果。 在小规模方案中,这似乎微不足道,但请考虑涉及大型数据集的情况,例如 Active Directory。 检索数千个用户帐户的详细信息,然后将其缩小到一个小子集,是一种效率较低的做法。 练习左侧筛选器,即尽可能早地在命令序列中应用筛选器,即使在看似微不足道的情况下也是如此。 这种习惯可确保在更复杂的方案中提高效率,在这种情况下,它变得更加重要。
用于有效筛选的命令排序
有一个误解,即 PowerShell 中的命令顺序是无关紧要的,但这是一个误解。 排列命令的顺序(尤其是在筛选时)非常重要。 例如,假设你正在使用 Select-Object
来选择特定属性并 Where-Object
进行筛选。 在这种情况下,必须首先应用筛选。 未能这样做意味着管道中可能没有必要的属性可用于筛选,从而导致无效或错误的结果。
下面的示例无法生成结果,因为当 通过管道传递给 Select-Object
时,Where-Object
属性不存在。 这是因为 CanPauseAndContinue 属性不包含在 Select-Object
所做的选择中。 实际上,它被排除或筛选掉。
Get-Service |
Select-Object -Property DisplayName, Running, Status |
Where-Object CanPauseAndContinue
反转 Select-Object
和 Where-Object
的顺序会产生所需的结果。
Get-Service |
Where-Object CanPauseAndContinue |
Select-Object -Property DisplayName, Status
DisplayName Status
----------- ------
Workstation Running
Netlogon Running
Hyper-V Heartbeat Service Running
Hyper-V Data Exchange Service Running
Hyper-V Remote Desktop Virtualization Service Running
Hyper-V Guest Shutdown Service Running
Hyper-V Volume Shadow Copy Requestor Running
Web Threat Defense Service Running
Web Threat Defense User Service_644de Running
Windows Management Instrumentation Running
管道
如本书中的许多示例所示,通常可以使用一个命令的输出作为另一个命令的输入。 在第 3 章中,Get-Member
用于确定命令生成的对象类型。
第 3 章还展示了如何使用 的 Get-Command
参数来确定哪些命令接受这种类型的输入。 根据命令帮助的详尽程度,它可能包括 INPUTS 和 OUTPUTS 部分。
INPUTS 表示可以将 ServiceController 或 String 对象通过管道连接到 Stop-Service
cmdlet。
help Stop-Service -Full
以下输出经过简化以显示帮助的相关部分。
...
INPUTS
System.ServiceProcess.ServiceController
You can pipe a service object to this cmdlet.
System.String
You can pipe a string that contains the name of a service to this
cmdlet.
OUTPUTS
None
By default, this cmdlet returns no output.
System.ServiceProcess.ServiceController
When you use the PassThru parameter, this cmdlet returns a
ServiceController object representing the service.
...
但是,它不指定哪些参数接受这种类型的输入。 可以通过检查 Stop-Service
cmdlet 帮助的完整版本中的不同参数来确定该信息。
help Stop-Service -Full
同样,下面的结果中只显示相关的帮助。 请注意,DisplayName 参数不接受管道输入。 InputObject 参数接受 ServiceController 对象的按值管道输入。 Name 参数接受字符串对象的按值管道输入和按属性名称管道输入。
...
-DisplayName <System.String[]>
Specifies the display names of the services to stop. Wildcard
characters are permitted.
Required? true
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? true
-InputObject <System.ServiceProcess.ServiceController[]>
Specifies ServiceController objects that represent the services to
stop. Enter a variable that contains the objects, or type a command
or expression that gets the objects.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByValue)
Accept wildcard characters? false
-Name <System.String[]>
Specifies the service names of the services to stop. Wildcard
characters are permitted.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? true
...
在处理管道输入时,如果一个参数同时接受按属性名称管道输入和按值管道输入,则优先考虑按值绑定。 如果此方法失败,则尝试按属性名称处理管道输入
例如,如果将生成 ServiceController 对象的命令的输出传递给 Stop-Service
,则此输出将绑定到 InputObject 参数。 如果管道命令生成一个 String 对象,则会将输出与 Name 参数相关联。 如果从一个不产生 ServiceController 或String 对象但包含名为 Name 的属性的命令通过管道传输,则 Stop-Service
会将 Name 属性的值绑定到其 Name 参数。
确定 Get-Service
命令生成的输出类型。
Get-Service -Name w32time | Get-Member
Get-Service
生成 ServiceController 对象类型。
TypeName: System.ServiceProcess.ServiceController
如 Stop-Service
cmdlet 的帮助所示,InputObject 参数可以通过管道按值接收 ServiceController 对象。 这意味着,当你通过管道将 Get-Service
cmdlet 的输出传输到 Stop-Service
时,由 生成的 Get-Service
对象会绑定到 的 Stop-Service
参数。
Get-Service -Name w32time | Stop-Service
现在,请尝试字符串输入。 通过管道将 w32time
传输到 Get-Member
以确认它是字符串。
'w32time' | Get-Member
TypeName: System.String
PowerShell 帮助文档说明,通过管道将字符串传输到 Stop-Service
时,它会按值绑定到 Name 参数。 进行实际测试以查看此操作:通过管道将字符串 w32time
传递给 Stop-Service
。 此示例演示如何 Stop-Service
将字符串 w32time
作为要停止的服务的名称进行处理。 执行以下命令,观察此绑定和命令的执行过程。
请注意,w32time
被括在单引号中。 在 PowerShell 中,最好对静态字符串使用单引号,为字符串包含需要扩展的变量保留双引号。 单引号告知 PowerShell 在不分析变量的情况下对内容进行字面处理。 此方法不仅确保脚本准确解释字符串,还提高了性能,因为 PowerShell 在处理单引号字符串时所需的处理功耗更少。
'w32time' | Stop-Service
创建一个自定义对象,根据 的 Stop-Service
参数按属性名称测试管道输入。
$customObject = [pscustomobject]@{
Name = 'w32time'
}
CustomObject 变量的内容是 PSCustomObject 对象类型,它包含名为 Name的属性。
$customObject | Get-Member
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=w32time
在 PowerShell 中使用变量(如此示例中的 $customObject
)时,如果需要将变量括在引号中,请务必使用双引号。 双引号允许变量扩展 - PowerShell 计算变量并使用其值。 例如,如果将 $customObject
括在双引号中,并通过管道将其传递给 Get-Member
,PowerShell 将处理 $customObject
的值。 相比之下,使用单引号会导致将字面量字符串 $customObject
传递到 Get-Member
,而不是传递变量的值。 这种区别对于需要评估变量值的场景非常重要。
将 $customObject
变量的内容通过管道传输到 Stop-Service
cmdlet 时,与 Name 参数的绑定是按属性名而不是按值进行的。 这是因为 $customObject
是一个对象,其中包含名为 Name的属性。 在此方案中,PowerShell 标识 中的 $customObject
属性,并将其值用于 的 Stop-Service
参数。
使用其他属性名称创建另一个自定义对象,例如 服务。
$customObject = [pscustomobject]@{
Service = 'w32time'
}
试图通过管道将 w32time
传输到 $customObject
来停止 Stop-Service
服务时出错。 管道绑定失败,因为 $customObject
不会生成 ServiceController 或 String 对象,并且不包含 Name 属性。
$customObject | Stop-Service
Stop-Service : Cannot find any service with service name
'@{Service=w32time}'.
At line:1 char:17
+ $customObject | Stop-Service
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (@{Service=w32time}:String) [
Stop-Service], ServiceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShe
ll.Commands.StopServiceCommand
当一个命令的输出属性名称与另一个命令的管道输入要求不匹配时,可以使用 Select-Object
重命名属性名称,以便它们正确排列。
在以下示例中,使用 Select-Object
将 Service 属性重命名为名为 Name的属性。
乍一看,此示例的语法可能显得较为复杂。 然而,需要理解的是,学习语法不仅仅是复制和粘贴代码。 请花点时间手动键入代码。 此动手练习可帮助你记住语法,并且会随着反复努力变得更加直观。 利用多个监视器或拆分屏幕也有助于学习过程。 在一个屏幕上显示示例代码,并在另一个屏幕上积极键入和测试。 通过此设置,您可以更轻松地跟进,并增强对语法的理解和记忆。
$customObject |
Select-Object -Property @{Name='Name';Expression={$_.Service}} |
Stop-Service
在某些情况下,可能需要使用不接受管道输入的参数。 在这种情况下,仍然可以使用一个命令的输出作为另一个命令的输入。 首先,将几个特定 Windows 服务的显示名称捕获并保存到文本文件中。 此步骤允许将保存的数据用作另一个命令的输入。
'Background Intelligent Transfer Service', 'Windows Time' |
Out-File -FilePath $env:TEMP\services.txt
可以使用括号将一个命令的输出作为参数的输入传递给另一个命令。
Stop-Service -DisplayName (Get-Content -Path $env:TEMP\services.txt)
此概念类似于代数中的运算顺序。 就像先计算括号中的数学运算一样,在外部命令之前执行括在括号中的命令。
PowerShellGet
PowerShellGet(PowerShell 5.0 及更高版本随附的模块)提供用于发现、安装、更新和发布 NuGet 存储库中的 PowerShell 模块和其他项的命令。 对于使用 PowerShell 3.0 及更高版本的用户,PowerShellGet 也可单独下载。
PowerShell 库是 Microsoft 托管的联机存储库,旨在作为共享 PowerShell 模块、脚本和其他资源的中心枢纽。 虽然 Microsoft 托管 PowerShell 库,但 PowerShell 社区贡献了大多数可用的模块和脚本。 鉴于这些模块和脚本的来源,在将 PowerShell 库中的任何代码集成到环境中之前,请谨慎行事。 在隔离的测试环境中查看和测试 PowerShell Gallery 中的下载项。 此过程可确保代码安全可靠,按预期工作,并保护环境免受因未经审查的代码引起的潜在问题或漏洞。
许多组织选择建立自己的内部专用 NuGet 存储库。 此存储库提供双重用途。 首先,它充当一个安全的位置,用于存储内部开发的模块,仅用于内部使用。 其次,它提供从外部采购的经过审查的模块集合,包括来自公共存储库的模块。 在将这些外部模块添加到内部存储库之前,公司通常会进行彻底的验证过程。 此过程对于确保模块不受恶意内容的影响,并与公司的安全性和操作标准保持一致非常重要。
使用属于 Find-Module
模块的 cmdlet,在 PowerShell 库中查找我编写的名为 MrToolkit 的模块。
Find-Module -Name MrToolkit
NuGet provider is required to continue
PowerShellGet requires NuGet provider version '2.8.5.201' or newer to
interact with NuGet-based repositories. The NuGet provider must be available
in 'C:\Program Files\PackageManagement\ProviderAssemblies' or
'C:\Users\mikefrobbins\AppData\Local\PackageManagement\ProviderAssemblies'.
You can also install the NuGet provider by running 'Install-PackageProvider
-Name NuGet -MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to
install and import the NuGet provider now?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):
Version Name Repository Description
------- ---- ---------- -----------
1.3 MrToolkit PSGallery Misc PowerShell Tools
首次使用 PowerShellGet 模块中的某个命令时,系统会提示你安装 NuGet 提供程序。
若要安装 MrToolkit 模块,请通过管道将上一个命令传递给 Install-Module
。
Find-Module -Name MrToolkit | Install-Module -Scope CurrentUser
Untrusted repository
You are installing the modules from an untrusted repository. If you trust
this repository, change its InstallationPolicy value by running the
Set-PSRepository cmdlet. Are you sure you want to install the modules from
'https://www.powershellgallery.com/api/v2'?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help
(default is "N"):y
由于 PowerShell 库是一个不受信任的存储库,因此它会提示你批准模块的安装。
以简单方式查找管道输入
MrToolkit 模块包括名为 Get-MrPipelineInput
的函数。 此 cmdlet 旨在为用户提供一种方便的方法,用于标识能够接受管道输入的命令的参数。 具体而言,它揭示了三个关键方面:
- 命令的哪些参数可以接收管道输入
- 每个参数接受的对象类型
- 不论它们是接受按值管道输入还是按属性名称管道输入
此功能极大地简化了了解和使用 PowerShell 命令的管道功能的过程。
可以使用此函数确定通过分析帮助文档之前获取的信息。
Get-MrPipelineInput -Name Stop-Service | Format-List
ParameterName : InputObject
ParameterType : System.ServiceProcess.ServiceController[]
ValueFromPipeline : True
ValueFromPipelineByPropertyName : False
ParameterName : Name
ParameterType : System.String[]
ValueFromPipeline : True
ValueFromPipelineByPropertyName : True
总结
在本章中,您了解了 PowerShell 单行命令的复杂性。 你还了解到,命令的实际行数与其作为 PowerShell 单行式命令的分类无关。 此外,你还了解了左侧筛选器、管道和 PowerShellGet 等关键概念。
回顾
- 什么是 PowerShell 单行式命令?
- 在 PowerShell 中,哪些字符可以出现自然换行?
- 为什么要使用左侧筛选器?
- PowerShell 命令可以接受管道输入的两种方式是什么?
- 为什么不能信任 PowerShell 库中找到的命令?
引用
后续步骤
在下一章中,你将了解格式、别名、提供者和比较运算符。