PowerShell 脚本编写全攻略(2025 版)
从“能跑”到“企业级可维护、可复用、可交付”的完整进化路线
1. 脚本基本结构(企业级必备模板)
#==========================================================================
# Script Name : Backup-Database.ps1
# Description : 每日自动备份指定数据库并清理旧备份
# Author : 张三 (zhang.san@company.com)
# Created : 2025-12-03
# Version : 2.1.0
#==========================================================================
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact = 'Medium'
)]
param (
[Parameter(Mandatory=$true)]
[string]$ServerInstance,
[Parameter(Mandatory=$true)]
[string[]]$DatabaseName,
[string]$BackupPath = "\\nas\backup\db",
[int]$RetentionDays = 30,
[switch]$WhatIf
)
# 严格模式(推荐所有正式脚本第一行就打开)
Set-StrictMode -Version Latest
# 停止任何非终止错误(配合 try/catch 才能捕获)
$ErrorActionPreference = 'Stop'
# 记录开始时间(用于日志和耗时统计)
$ScriptStartTime = Get-Date
$LogTime = { (Get-Date).ToString('yyyy-MM-dd HH:mm:ss') }
Write-Verbose "$(&$LogTime) 脚本开始执行..."
2. 必须养成的 10 个黄金习惯
| 习惯 | 正确写法 | 错误写法 |
|---|---|---|
| 1. 总是加参数块 | param(…) | 直接在脚本里写 $args |
| 2. 给参数加类型和验证 | [ValidateScript({Test-Path $_})][string]$Path | 裸 string |
| 3. 使用 CmdletBinding | [CmdletBinding(SupportsShouldProcess=$true)] | 没有 |
| 4. 使用 Write-Verbose 而不是 Write-Host | Write-Verbose “正在连接 $Server” | Write-Host |
| 5. 错误处理统一用 try/catch | try { … } catch { Write-Error $_ ; return } | 直接让错误打印 |
| 6. 所有函数都用高级函数写法 | function Backup-OneDB { [CmdletBinding()] param(…) … } | function Backup-OneDB { … } |
| 7. 路径全部用 Join-Path | Join-Path $BackupPath $FileName | “$BackupPath\$FileName” |
| 8. 脚本结束统一写总结 | Write-Host “耗时 $((Get-Date)-$ScriptStartTime)” -ForegroundColor Green | 直接结束 |
| 9. 脚本开头写帮助注释 | .SYNOPSIS / .EXAMPLE | 没有注释 |
| 10. 用 ShouldProcess 实现 -WhatIf/-Confirm | if ($PSCmdlet.ShouldProcess($db, “备份数据库”)) { … } | 自己判断 $WhatIf |
3. 企业级脚本必备功能模块(直接复制粘贴)
# 1. 日志函数(支持文件+控制台彩色)
function Write-Log {
param(
[Parameter(Mandatory)] [string]$Message,
[ValidateSet('INFO','WARN','ERROR','DEBUG')] [string]$Level = 'INFO'
)
$color = switch($Level){
'INFO' {'White'}
'WARN' {'Yellow'}
'ERROR' {'Red'}
'DEBUG' {'Cyan'}
}
$logLine = "$(&$LogTime) [$Level] $Message"
Write-Host $logLine -ForegroundColor $color
$logLine | Out-File -FilePath "$PSScriptRoot\$(Split-Path $PSCommandPath -Leaf).log" -Append -Encoding UTF8
}
# 2. 统一的错误处理函数
function Handle-Error {
param($ErrorRecord = $_)
Write-Log "错误: $($ErrorRecord.Exception.Message)" 'ERROR'
Write-Log "位置: $($ErrorRecord.InvocationInfo.ScriptLineNumber)" 'ERROR'
exit 1
}
# 3. 主流程写在函数里(推荐)
function Main {
try {
foreach ($db in $DatabaseName) {
if ($PSCmdlet.ShouldProcess($db, "备份数据库到 $BackupPath")) {
Backup-OneDatabase -Server $ServerInstance -Database $db -Path $BackupPath
}
}
Remove-OldBackups -Path $BackupPath -OlderThanDays $RetentionDays
}
catch { Handle-Error $_ }
}
# 4. 脚本最后统一调用
Main
Write-Log "脚本执行完成,耗时 $(((Get-Date) - $ScriptStartTime).ToString('hh\:mm\:ss'))" 'INFO'
4. 参数验证高级用法(让脚本“傻瓜式”)
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$ComputerName,
[ValidateRange(1,65535)]
[int]$Port = 1433,
[ValidatePattern('^(\d{1,3}\.){3}\d{1,3}$')]
[string]$IPAddress,
[ValidateScript({ $_ -in (Get-ChildItem 'C:\Scripts\Configs\*.json').Name })]
[string]$ConfigFile,
[ValidateSet('Full','Differential','Log')]
[string]$BackupType = 'Full'
)
5. 脚本签名(生产环境必须)
# 1. 获取企业代码签名证书(一次设置,永久有效)
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1
# 2. 对所有 .ps1 文件批量签名
Get-ChildItem *.ps1 -Recurse | Set-AuthenticodeSignature -Certificate $cert
# 3. 设置执行策略(推荐)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine # 允许运行本地签名脚本
Set-ExecutionPolicy AllSigned -Scope LocalMachine # 最严格,所有脚本都必须签名
6. 打包成模块(终极复用方式)
目录结构:
MyTools/
│ MyTools.psd1
│ MyTools.psm1
└───Public
│ Backup-Database.ps1
│ Restore-Database.ps1
└───Private
│ Write-Log.ps1
发布到公司内部 PowerShell Gallery:
Publish-Module -Path .\MyTools -Repository CompanyRepo -NuGetApiKey "xxx"
7. 一键部署脚本(推荐用这个模板)
# Deploy.ps1(双击即可部署)
$Source = "\\fileserver\scripts\Production"
$Target = "C:\Scripts"
if (!(Test-Path $Target)) { New-Item -Path $Target -ItemType Directory -Force }
Copy-Item "$Source\*.*" $Target -Recurse -Force
# 自动签名(如果有证书)
if ($cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert) {
Get-ChildItem "$Target\*.ps1" | Set-AuthenticodeSignature -Cert $cert
}
Write-Host "部署完成!路径: $Target" -ForegroundColor Green
终极总结:企业级 PowerShell 脚本检查清单(发版前必过)
- [ ] 加了 param 块和 [CmdletBinding()]
- [ ] 所有参数都有类型和验证
- [ ] 使用了 Write-Verbose / Write-Log,不用 Write-Host 刷屏
- [ ] 所有外部命令都在 try/catch 里且加了 -ErrorAction Stop
- [ ] 支持 -WhatIf 和 -Confirm
- [ ] 脚本已数字签名
- [ ] 写好了帮助注释(Get-Help 可显示)
- [ ] 路径全部用 Resolve-Path / Join-Path
- [ ] 已打包成模块或放在版本控制里
- [ ] 在测试环境通过了 Pester 测试
把上面这些全部做到,你的 PowerShell 脚本就从“个人工具”进化成了“企业级自动化资产”。
需要我帮你把现有的脚本直接重构为企业级模板,或者帮你写 Pester 测试用例,随时贴代码,我直接给你改好!