第4天:模块、脚本编写、错误处理与 .NET 集成
🎯 学习目标
- 掌握 PowerShell 脚本(
.ps1)的结构与最佳实践 - 理解模块(Module)的作用,并能创建和导入自定义模块
- 熟练使用
try/catch/finally进行结构化错误处理 - 直接调用 .NET 类库(如
System.IO,System.Net等)完成高级任务
一、PowerShell 脚本编写(.ps1 文件)
1. 脚本基本结构
一个良好的脚本应包含:
- 注释说明(用途、作者、参数)
- 参数声明(
param块) - 错误处理(
$ErrorActionPreference或try/catch) - 主逻辑
- 清理或输出
bash
# Get-DiskUsage.ps1
<#
.SYNOPSIS
获取指定目录的磁盘使用情况。
.DESCRIPTION
计算目录下所有文件总大小,支持递归。
.PARAMETER Path
目标路径,默认为当前目录。
.EXAMPLE
.\Get-DiskUsage.ps1 -Path C:\Temp
#>
param(
[string]$Path = "."
)
if (-not (Test-Path $Path)) {
throw "路径不存在: $Path"
}
$totalBytes = (Get-ChildItem -Path $Path -Recurse -File | Measure-Object -Property Length -Sum).Sum
"{0:N2} MB" -f ($totalBytes / 1MB)
💡 使用方式:
bash.\Get-DiskUsage.ps1 -Path C:\Windows
2. 脚本执行方式回顾
| 方式 | 命令 | 说明 |
|---|---|---|
| 当前作用域运行 | . .\script.ps1 |
变量/函数留在当前会话(点源执行) |
| 子作用域运行 | .\script.ps1 |
执行完后环境不保留 |
| 绕过策略运行 | PowerShell -ExecutionPolicy Bypass -File script.ps1 |
适合自动化 |
二、模块(Modules)------组织可重用代码
1. 什么是模块?
模块是 PowerShell 中用于封装函数、变量、别名等的单元,便于分发和复用。
内置模块示例:
bash
Get-Module -ListAvailable # 查看所有可用模块
Import-Module ActiveDirectory # 导入 AD 模块(需安装)
2. 创建自定义模块
步骤1:创建模块目录
bash
C:\Users\<username>\Documents\Documents\WindowsPowerShell\PowerShell\Modules\MyTools\
└── MyTools.psm1 ← 模块主文件
⚠️ 路径必须在
$env:PSModulePath中(用户模块默认在此)⚠️ 可能次路径不存在,需手动建立
步骤2:编写 MyTools.psm1
bash
# MyTools.psm1
function Get-Uptime {
$os = Get-CimInstance Win32_OperatingSystem
(Get-Date) - $os.LastBootUpTime
}
function Test-Port {
param([string]$ComputerName, [int]$Port)
try {
$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.Connect($ComputerName, $Port)
$tcp.Connected
$tcp.Close()
} catch {
$false
}
}
Export-ModuleMember -Function Get-Uptime, Test-Port
步骤3:导入并使用
bash
Import-Module MyTools
Get-Uptime
Test-Port -ComputerName "baidu.com" -Port 80
✅ 优势:函数自动加载,命名空间清晰,易于共享。
三、错误处理:try / catch / finally
PowerShell 支持结构化异常处理,但需注意:
- 仅对"终止性错误"(Terminating Errors)生效
- 默认很多命令产生的是"非终止错误"(如
Get-Item NoSuchFile)
1. 强制将错误转为终止错误
使用 -ErrorAction Stop:
bash
try {
Get-Item "C:\NonExistentFile.txt" -ErrorAction Stop
} catch {
Write-Host "捕获到错误: $($_.Exception.Message)" -ForegroundColor Red
} finally {
Write-Host "清理操作(无论成功与否都会执行)"
}
2. 捕获特定异常类型(.NET 集成)
你可以根据 .NET 异常类型进行精细处理:
bash
try {
[System.IO.File]::ReadAllText("C:\Protected\file.txt")
} catch [System.UnauthorizedAccessException] {
Write-Host "权限不足!" -ForegroundColor Yellow
} catch [System.IO.FileNotFoundException] {
Write-Host "文件未找到!" -ForegroundColor Cyan
} catch {
Write-Host "未知错误: $($_.Exception.GetType().FullName)"
}
🔍 提示:通过
$_.Exception.GetType().FullName查看具体异常类型。
四、PowerShell 与 .NET 深度集成(核心优势!)
PowerShell 基于 .NET(Windows 上是 .NET Framework/.NET,跨平台是 .NET Core),可直接调用任何 .NET 类。
1. 调用静态方法
bash
# 获取当前时间(等价于 Get-Date)
[System.DateTime]::Now
# 生成 GUID
[System.Guid]::NewGuid()
# 环境信息
[System.Environment]::OSVersion
[System.Environment]::GetFolderPath("Desktop")
2. 创建 .NET 对象实例
bash
# 创建 WebClient(已过时,推荐使用 Invoke-WebRequest)
$client = New-Object System.Net.WebClient
$content = $client.DownloadString("https://httpbin.org/ip")
$client.Dispose() # 释放资源
✅ 更现代的方式(PowerShell 5+):
bashInvoke-RestMethod https://httpbin.org/ip
3. 使用 .NET 枚举
bash
# 文件属性
$file = Get-Item .\test.txt
$file.Attributes -band [System.IO.FileAttributes]::Hidden
4. 调用 .NET 方法处理字符串/数学
bash
# 字符串操作(比 PowerShell 内置更强大)
[System.String]::IsNullOrWhiteSpace(" ") # True
# 数学计算
[Math]::Round(3.14159, 2) # 3.14
[Math]::Pow(2, 10) # 1024
5. 实战:用 .NET 发送邮件(无需 Outlook)
bash
$smtp = New-Object Net.Mail.SmtpClient("smtp.example.com", 587)
$smtp.EnableSsl = $true
$smtp.Credentials = [Net.NetworkCredential]::new("user@example.com", "password")
$mail = [Net.Mail.MailMessage]::new("from@example.com", "to@example.com", "主题", "正文")
$smtp.Send($mail)
$smtp.Dispose()
⚠️ 注意:生产环境建议使用安全凭据(如
$PSCredential)。
五、动手实践:综合项目
项目:编写一个带错误处理的"网站健康检查"脚本
bash
# HealthCheck.ps1
param(
[string[]]$Urls = @("https://baidu.com", "https://github.com")
)
foreach ($url in $Urls) {
try {
Write-Host "正在检查: $url" -ForegroundColor Green
$response = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10
if ($response.StatusCode -eq 200) {
Write-Host "✅ 正常 (状态码: $($response.StatusCode))"
} else {
Write-Host "⚠️ 异常状态码: $($response.StatusCode)" -ForegroundColor Yellow
}
} catch {
$ex = $_.Exception
if ($ex -is [System.Net.WebException]) {
Write-Host "❌ 网络错误: $($ex.Message)" -ForegroundColor Red
} else {
Write-Host "💥 未知错误: $($ex.Message)" -ForegroundColor Magenta
}
}
}
运行:
bash
.\HealthCheck.ps1 -Urls "https://baidu.com", "https://nonexistent.fake"
六、今日重点总结
- ✅ 脚本应结构清晰,包含参数、注释和错误处理
- ✅ 模块(
.psm1)是组织可重用函数的最佳方式 - ✅
try/catch需配合-ErrorAction Stop或 .NET 异常使用 - ✅ PowerShell 是 .NET 的"脚本前端",可直接调用任何 .NET 类库
- ✅ 利用
[System.xxx]和New-Object解锁高级功能(网络、文件、加密等)
📚 参考资料(Microsoft Learn)
- About Modules
- About Try Catch Finally
- Calling .NET Methods
- .NET API Browser
🏁 课后作业
- 创建一个名为
MathUtils的模块,包含两个函数:Convert-Temperature(℃↔℉)和Get-Factorial(阶乘,用递归或 .NETBigInteger)。 - 编写脚本:尝试读取一个受保护的系统文件(如
C:\Windows\System32\config\SAM),使用try/catch捕获UnauthorizedAccessException并友好提示。 - 使用 .NET 的
System.IO.Compression.ZipFile类,编写一个函数Compress-Folder,将指定文件夹压缩为 ZIP。