PowerShell 脚本最佳实践指南

什么是

本文提供了 PowerShell 脚本的全面最佳实践指南,重点介绍代码结构、输出格式、错误处理、性能优化及安全措施。

为什么

遵循 PowerShell 脚本的最佳实践可以确保脚本具备良好的可读性、可维护性、安全性和高效性。它有助于减少技术债务,提升团队协作效率,并降低生产环境中的风险。

如何实施

工具与控制器设计

判断你在编写“工具”还是“控制器”

  • 工具:可复用的函数或模块。
  • 控制器:用于自动化特定任务,非复用型脚本。

使代码模块化

  • 利用函数和脚本模块提升复用性。

使用标准命名规范

  • 遵循 动词-名词 格式,使用 PowerShell 官方动词(Get-Verb)。

参数命名标准化

  • 使用如 $ComputerName 这样的通用名称,避免自定义前缀。

工具应输出原始数据

  • 保持数据的灵活性,避免过度处理。

控制器应输出格式化数据

  • 提供用户友好的报告格式。

示例

function Get-DiskInfo {
    param ([string]$ComputerName)
    Get-WmiObject Win32_LogicalDisk -ComputerName $ComputerName
}

避免重复造轮子

优先使用内置 cmdlet,如 Test-Connection,而非自定义功能。

# 推荐
Test-Connection $ComputerName -Quiet

编写参数块

始终编写帮助文档

使用注释形式的 .SYNOPSIS.DESCRIPTION 和至少一个 .EXAMPLE

function Test-Help {
    <#
        .SYNOPSIS
            展示正确的帮助文档格式。
        .EXAMPLE
            Test-Help -MandatoryParameter "示例"
            使用必需参数运行 Test-Help 函数。
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("MP")]
        [String]$MandatoryParameter
    )
}

使用 [CmdletBinding()]

启用如 -Verbose-Debug-ErrorAction 等通用参数。

支持 -WhatIf 和 -Confirm

涉及状态变更的命令应使用 SupportsShouldProcess

[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")]
param ([switch]$Force)

强类型参数

明确指定参数类型,提升代码的可靠性与可读性。

param (
    [string]$Name,
    [int]$Count
)

正确使用 [switch]

  • 默认值为 $false
  • 仅作为布尔值处理,避免三态逻辑。

输出格式化

除非必要,避免使用 Write-Host

根据场景选择 Write-VerboseWrite-DebugWrite-Output

使用 Write-Progress 显示进度

Write-Progress -Activity "处理中" -Status "完成50%" -PercentComplete 50

为自定义对象使用格式文件

通过 .format.ps1xml 文件定义格式,而非内联格式化。

每次仅输出一种对象类型

使用 [OutputType()],避免类型混合。


错误处理最佳实践

cmdlet 中使用 -ErrorAction Stop

确保通过 try-catch 捕获错误。

try {
    Get-Item "C:\无效路径" -ErrorAction Stop
} catch {
    Write-Warning "未找到指定项。"
}

对非 cmdlet 操作使用 $ErrorActionPreference

在风险操作前临时设置为 'Stop'

避免使用标志位和 $? 判断错误

优先使用结构化的 try-catch 处理机制。

在 catch 中立即复制 $Error[0] 或 $_

catch {
    $errorDetails = $_
    Write-Error "发生错误: $($errorDetails.Exception.Message)"
}

性能优化

PERF-01 需要时再衡量性能

利用 Measure-Command 评估不同方案。

Measure-Command {
    foreach ($item in $data) { Process-Item $item }
}

PERF-02 平衡性能与可读性

  • 小型数据集优先考虑可读性。
  • 大型数据集时采用流处理或 .NET 技术。

可读性优先:

$content = Get-Content -Path file.txt
foreach ($line in $content) {
    Do-Something -Input $line
}

性能优化:

Get-Content -Path file.txt | ForEach-Object {
    Do-Something -Input $_
}

使用 .NET 提升性能:

$sr = New-Object System.IO.StreamReader "file.txt"
while ($sr.Peek() -ge 0) {
    $line = $sr.ReadLine()
    Do-Something -Input $line
}

PERF-03 优先使用语言特性

  • 语言结构(如 foreach)> .NET 方法 > 脚本 > cmdlet/管道。
  • 避免过早优化,务必测量后再优化。

安全性最佳实践

始终使用 PSCredential 管理凭据

避免明文密码,使用 [Credential()] 参数。

param (
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    $Credential
)

调用 API 时:

$Insecure.SetPassword($Credential.GetNetworkCredential().Password)

使用 SecureString 处理敏感数据

$Secure = Read-Host -Prompt "请输入敏感数据" -AsSecureString

安全转换为明文:

$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Secure)
$PlainText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)

安全保存凭据

Get-Credential | Export-CliXml -Path C:\secure\cred.xml
$Credential = Import-CliXml -Path C:\secure\cred.xml

保存加密字符串

ConvertFrom-SecureString -SecureString $Secure | Out-File -Path "${Env:AppData}\secure.bin"
$Secure = Get-Content -Path "${Env:AppData}\secure.bin" | ConvertTo-SecureString

结论

通过遵循以上 PowerShell 最佳实践,您可以编写出稳健、高效且安全的脚本,适用于各种自动化场景。始终在可读性、性能与安全性之间寻求最佳平衡,打造高质量的解决方案。

标题和URL已复制