跳到主要内容

PowerShell 进阶

本篇在基础篇之上,讲解模块、对象、错误处理、脚本编写、数据格式与自动化。 建议先掌握变量、管道、流程控制和函数。


1. 模块(Module)

模块是打包好的命令集合,用于扩展 PowerShell 功能。

Get-Module # 查看当前会话已加载的模块
Get-Module -ListAvailable # 查看系统已安装的模块
Import-Module Pester # 导入模块

从 PowerShell Gallery(在线仓库)安装第三方模块:

Find-Module -Name PSReadLine # 搜索模块
Install-Module -Name Pester -Scope CurrentUser # 安装
Update-Module -Name Pester # 更新

首次安装可能提示需要 NuGet 提供程序或信任仓库,输入 Y 确认即可。


2. 对象与成员

PowerShell 中命令返回的是对象,不只是文本。用 Get-Member 查看对象有哪些属性和方法。

Get-Process | Get-Member # 查看进程对象的成员
Get-Date | Get-Member -MemberType Method # 只看方法

访问属性与方法:

$p = Get-Process -Name explorer
$p.Name # 属性
$p.CPU # 属性
$p.Kill() # 方法(结束进程)

2.1 自定义对象

$obj = [PSCustomObject]@{
Name = "张三"
Age = 18
}
$obj.Name # 张三

2.2 计算属性

Select-Object 可以基于现有属性计算出新属性:

Get-Process |
Select-Object Name,
@{Name = "内存MB"; Expression = { [math]::Round($_.WorkingSet / 1MB, 2) }} |
Sort-Object 内存MB -Descending |
Select-Object -First 5

3. 错误处理

3.1 try / catch / finally

try {
$content = Get-Content -Path "不存在的文件.txt" -ErrorAction Stop
}
catch {
Write-Output "出错了:$($_.Exception.Message)"
}
finally {
Write-Output "无论如何都会执行"
}

要让 Get-Content 等命令触发 catch,需加 -ErrorAction Stop,否则非终止性错误不会进入 catch。

3.2 错误处理偏好

$ErrorActionPreference = "Stop" # 全局:所有错误都当作终止性错误
$ErrorActionPreference = "Continue" # 默认:继续执行

Get-ChildItem -ErrorAction SilentlyContinue # 忽略错误,静默继续

3.3 错误信息对象

try {
1 / 0
}
catch {
$_.Exception.Message # 错误消息
$_.ScriptStackTrace # 调用栈
$_.InvocationInfo.Line # 出错的那行
}

4. 编写与运行脚本

4.1 脚本文件

把命令保存为 .ps1 文件即可成为脚本:

# hello.ps1
param(
[string]$Name = "世界"
)
Write-Output "你好,$Name"

运行:

.\hello.ps1 # 当前位置运行
.\hello.ps1 -Name "张三"
powershell -ExecutionPolicy Bypass -File .\hello.ps1 # 绕过执行策略

4.2 参数进阶

param(
[Parameter(Mandatory = $true)] # 必填
[string]$Path,

[ValidateSet("Dev", "Prod")] # 限定取值
[string]$Env = "Dev",

[switch]$Verbose # 开关参数(不带值)
)

if ($Verbose) { Write-Output "详细模式" }

调用:

.\deploy.ps1 -Path "C:\app" -Env Prod -Verbose

4.3 支持管道的函数

使用 begin / process / end 三个块处理管道输入:

function Get-Even {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[int]$Number
)
begin { Write-Output "开始处理" }
process { if ($Number % 2 -eq 0) { Write-Output $Number } }
end { Write-Output "处理结束" }
}

1..10 | Get-Even

5. 文本与正则

$text = "订单号:A12345,金额:99.5 元"

# 提取数字
$text -match "\d+" # True
$Matches[0] # 第一个匹配

# 提取所有匹配
[regex]::Matches($text, "\d+") | ForEach-Object { $_.Value }

# 替换
$text -replace "金额:\d+(\.\d+)?", "金额:***"

# 拆分
"a,b,c" -split ","

-replace-match 默认使用正则表达式;-like 使用通配符。


6. 数据格式:CSV 与 JSON

6.1 CSV

# 读取 CSV(自动转为对象)
$rows = Import-Csv -Path "users.csv"
$rows | Where-Object { $_.Age -gt 18 }

# 导出 CSV
$data | Export-Csv -Path "out.csv" -NoTypeInformation -Encoding UTF8

6.2 JSON

# 字符串 → 对象
$json = '{"name":"张三","age":18}'
$obj = $json | ConvertFrom-Json
$obj.name # 张三

# 对象 → JSON
$obj | ConvertTo-Json -Depth 5

# 读写文件
Get-Content "config.json" -Raw | ConvertFrom-Json
$obj | ConvertTo-Json | Set-Content "config.json" -Encoding UTF8

6.3 其他格式

Get-Process | Export-Clixml out.xml # XML 序列化
Import-Clixml out.xml # 反序列化

7. 文件与目录进阶

# 递归查找并删除 30 天前的日志
Get-ChildItem -Path ".\logs" -Filter "*.log" -Recurse |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -Force

# 批量重命名
Get-ChildItem -Filter "*.txt" | Rename-Item -NewName { $_.Name -replace " ", "_" }

# 计算目录总大小
(Get-ChildItem -Recurse -File | Measure-Object Length -Sum).Sum / 1MB

# 查找大文件
Get-ChildItem -Recurse -File | Sort-Object Length -Descending | Select-Object -First 10

8. 远程与自动化

8.1 远程执行(WinRM)

# 在远程主机执行命令
Invoke-Command -ComputerName Server01 -ScriptBlock { Get-Process }

# 建立交互式会话
Enter-PSSession -ComputerName Server01
Exit-PSSession

需目标主机启用 WinRM(Enable-PSRemoting -Force)并具备凭据。

8.2 后台任务与作业

$job = Start-Job -ScriptBlock { Start-Sleep 5; "完成" }
Get-Job
Receive-Job -Id $job.Id -Wait
Remove-Job -Id $job.Id

8.3 计划任务

$action = New-ScheduledTaskAction -Execute "pwsh" -Argument "-File C:\scripts\backup.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 3am
Register-ScheduledTask -TaskName "每日备份" -Action $action -Trigger $trigger

9. 性能与最佳实践

  1. 优先使用管道与内置命令,避免手写循环(性能更好)。
  2. 筛选尽量前置Get-ChildItem -Filter| Where-Object 快。
  3. 大量输出时避免字符串拼接,改用数组或 [System.Text.StringBuilder]
  4. 脚本开头写 [CmdletBinding()],获得 -Verbose-Debug 等通用参数。
  5. 使用 #Requires -Version 7 声明脚本所需的最低版本。
  6. 变量命名用有意义的名字,函数用「动词-名词」并确保动词在 Get-Verb 列表中。
#Requires -Version 7.0
[CmdletBinding()]
param(
[string]$Path = "."
)

10. 实用片段合集

测速 / 计时

Measure-Command { 1..100000 | ForEach-Object { $_ * 2 } }

生成随机密码

-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 16 | ForEach-Object { [char]$_ })

查看端口占用

Get-NetTCPConnection -LocalPort 8080 | Select-Object LocalAddress, State, OwningProcess

递归列出目录树

Get-ChildItem -Recurse | Where-Object { $_.PSIsContainer } | Select-Object FullName

发送 HTTP 请求

$resp = Invoke-RestMethod -Uri "https://api.github.com/repos/PowerShell/PowerShell"
$resp.stargazers_count

11. 学习资源

结合基础篇反复练习,多写小脚本解决日常重复工作,是掌握 PowerShell 最快的路径。