适用于 PowerShell 5.1 及以上版本
在安全审计、故障排查、培训演示等场景中,记录操作过程至关重要。PowerShell 的 Start-Transcript 可以记录控制台的完整输入输出,包括命令、结果、错误信息。结合日志轮转和自动化脚本,可以构建完整的操作审计系统,确保所有关键操作可追溯、可回放。
本文将讲解 PowerShell 会话录制的技术和实用方案。
基础会话录制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Start-Transcript -Path "C:\Transcripts\session-$(Get-Date -Format 'yyyyMMdd_HHmmss').txt" -Append Write-Host "会话录制已开始" -ForegroundColor Green
Get-Process | Select-Object -First 3 Name, Id, CPU Write-Host "当前时间:$(Get-Date)" -ForegroundColor Cyan
Stop-Transcript Write-Host "会话录制已停止" -ForegroundColor Yellow
$latestTranscript = Get-ChildItem "C:\Transcripts" -Filter "*.txt" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Write-Host "`n最新录制文件:$($latestTranscript.Name) ($([math]::Round($latestTranscript.Length / 1KB, 1)) KB)"
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 会话录制已开始 ********************** Windows PowerShell transcript start Start time: 20250829103015 Username: CONTOSO\admin Runspace Id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Name Id CPU ---- -- --- chrome 12345 1250.3 node 12346 456.7 pwsh 12347 123.4
当前时间:2025-08-29 10:30:20 Windows PowerShell transcript end End time: 20250829103021 ********************** 会话录制已停止
最新录制文件:session-20250829_103015.txt (2.3 KB)
|
自动录制配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| function Install-AutoTranscript { $profileDir = Split-Path $PROFILE -Parent if (-not (Test-Path $profileDir)) { New-Item $profileDir -ItemType Directory -Force | Out-Null }
$transcriptBlock = @"
# ===== 自动会话录制 ===== `$transcriptDir = "`$env:USERPROFILE\Transcripts" if (-not (Test-Path `$transcriptDir)) { New-Item `$transcriptDir -ItemType Directory -Force | Out-Null } `$dateStamp = Get-Date -Format 'yyyyMMdd_HHmmss' `$transcriptFile = Join-Path `$transcriptDir "transcript-`$dateStamp.txt" Start-Transcript -Path `$transcriptFile -Append # ===== 自动会话录制结束 ===== "@
if (-not (Test-Path $PROFILE)) { Set-Content -Path $PROFILE -Value $transcriptBlock -Encoding UTF8 } else { $content = Get-Content $PROFILE -Raw if ($content -notmatch 'Start-Transcript') { Add-Content -Path $PROFILE -Value $transcriptBlock -Encoding UTF8 } }
Write-Host "自动录制已配置到 $PROFILE" -ForegroundColor Green Write-Host "下次启动 PowerShell 时自动开始录制" -ForegroundColor Yellow }
Install-AutoTranscript
function Get-TranscriptSummary { param([string]$Path = "$env:USERPROFILE\Transcripts")
$transcripts = Get-ChildItem $Path -Filter "transcript-*.txt" -ErrorAction SilentlyContinue
if (-not $transcripts) { Write-Host "未找到录制文件" -ForegroundColor Yellow return }
Write-Host "录制文件概览:" -ForegroundColor Cyan Write-Host " 总文件数:$($transcripts.Count)" $totalSize = [math]::Round(($transcripts | Measure-Object Length -Sum).Sum / 1MB, 2) Write-Host " 总大小:$totalSize MB"
$transcripts | Sort-Object LastWriteTime -Descending | Select-Object -First 5 | ForEach-Object { $sizeKB = [math]::Round($_.Length / 1KB, 1) Write-Host " $($_.Name) ($sizeKB KB) - $($_.LastWriteTime.ToString('yyyy-MM-dd HH:mm'))" -ForegroundColor DarkGray } }
Get-TranscriptSummary
|
执行结果示例:
1 2 3 4 5 6 7 8 9
| 自动录制已配置到 C:\Users\admin\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 下次启动 PowerShell 时自动开始录制
录制文件概览: 总文件数:45 总大小:12.5 MB transcript-20250829_103015.txt (2.3 KB) - 2025-08-29 10:30 transcript-20250829_090000.txt (5.6 KB) - 2025-08-29 09:00 transcript-20250828_140530.txt (8.2 KB) - 2025-08-28 14:05
|
录制文件清理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function Clear-OldTranscripts { param( [string]$Path = "$env:USERPROFILE\Transcripts", [int]$KeepDays = 90, [switch]$WhatIf )
$cutoff = (Get-Date).AddDays(-$KeepDays)
$oldFiles = Get-ChildItem $Path -Filter "transcript-*.txt" | Where-Object { $_.LastWriteTime -lt $cutoff }
if (-not $oldFiles) { Write-Host "没有需要清理的录制文件" -ForegroundColor Green return }
$totalSize = [math]::Round(($oldFiles | Measure-Object Length -Sum).Sum / 1MB, 2)
Write-Host "找到 $($oldFiles.Count) 个超过 $KeepDays 天的录制文件($totalSize MB)" -ForegroundColor Yellow
if ($WhatIf) { Write-Host "[WhatIf] 将删除以上文件" -ForegroundColor Yellow return }
$oldFiles | Remove-Item -Force Write-Host "已清理 $($oldFiles.Count) 个文件,释放 $totalSize MB" -ForegroundColor Green }
Clear-OldTranscripts -KeepDays 90 -WhatIf
|
执行结果示例:
1 2
| 找到 12 个超过 90 天的录制文件(3.45 MB) [WhatIf] 将删除以上文件
|
结构化操作日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| function Invoke-LoggedOperation { param( [Parameter(Mandatory)] [string]$OperationName,
[Parameter(Mandatory)] [scriptblock]$Operation,
[string]$LogDir = "C:\Logs\Operations" )
New-Item $LogDir -ItemType Directory -Force | Out-Null
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss' $transcriptPath = Join-Path $LogDir "$OperationName-$timestamp.txt"
$result = [PSCustomObject]@{ Operation = $OperationName StartTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' Status = "Unknown" Duration = "" Transcript = $transcriptPath Error = "" }
$sw = [System.Diagnostics.Stopwatch]::StartNew()
try { Start-Transcript -Path $transcriptPath -Force | Out-Null & $Operation Stop-Transcript | Out-Null
$result.Status = "Success" } catch { Stop-Transcript | Out-Null $result.Status = "Failed" $result.Error = $_.Exception.Message } finally { $sw.Stop() $result.Duration = "$($sw.Elapsed.ToString('mm\:ss'))" }
$logEntry = "[$($result.StartTime)] [$($result.Status)] $($result.Operation) ($($result.Duration))" Add-Content (Join-Path $LogDir "operations.log") -Value $logEntry -Encoding UTF8
$color = if ($result.Status -eq "Success") { "Green" } else { "Red" } Write-Host $logEntry -ForegroundColor $color
return $result }
Invoke-LoggedOperation -OperationName "Deploy-MyApp" -Operation { Write-Host "停止服务..." -ForegroundColor Cyan Stop-Service "MyApp" -ErrorAction SilentlyContinue
Write-Host "复制文件..." -ForegroundColor Cyan Copy-Item "C:\Releases\MyApp\*" "D:\Apps\MyApp\" -Recurse -Force
Write-Host "启动服务..." -ForegroundColor Cyan Start-Service "MyApp"
Write-Host "验证..." -ForegroundColor Cyan $svc = Get-Service "MyApp" Write-Host "服务状态:$($svc.Status)" }
|
执行结果示例:
1 2 3 4 5 6
| 停止服务... 复制文件... 启动服务... 验证... 服务状态:Running [2025-08-29 10:35:20] [Success] Deploy-MyApp (01:23)
|
注意事项
- 敏感信息:Transcript 会记录所有控制台输出,包括密码输入。敏感操作后及时清理或加密存储
- 磁盘空间:长期开启自动录制会积累大量文件,配置定期清理策略
- 性能影响:Transcript 的性能开销很小,但在极端高频输出场景中可能有影响
- 编码问题:Transcript 文件使用系统默认编码,中文内容可能需要 UTF-8 转换
- 嵌套限制:已经在一个 Transcript 中时不能再次
Start-Transcript(除非加 -Append)
- 远程会话:通过 Enter-PSSession 进入的远程会话需要单独配置 Transcript