适用于 PowerShell 7.0 及以上版本
Polyglot Notebooks(前身 .NET Interactive)是微软推出的一款交互式笔记本工具,它将 Jupyter 笔记本的理念带入了 .NET 生态。与传统的 Jupyter 仅支持 Python 等单一内核不同,Polyglot Notebooks 的核心优势在于多语言互操作:你可以在同一个笔记本中无缝切换 PowerShell、C#、F#、JavaScript、HTML 等语言,并通过变量共享机制让不同语言的代码块协同工作。
对于 PowerShell 用户而言,这意味着你不必再在”用 PowerShell 写脚本”和”用 Jupyter 做数据分析”之间做选择。你可以直接在笔记本中用 PowerShell 获取系统数据、调用 REST API、操作文件系统,然后用 C# 处理复杂的计算逻辑,最后用 JavaScript 生成可交互的可视化图表——所有这些都在同一个文档中完成。
本文将从三个方面介绍 Polyglot Notebooks 的实战用法:首先搭建环境并掌握基础的 Magic Commands,然后演示多语言协作与变量共享的技巧,最后通过一个数据分析实战案例展示完整的工作流。
Notebook 基础操作 要在 Polyglot Notebooks 中使用 PowerShell,需要先安装 .NET SDK 和 Polyglot Notebooks 扩展。整个过程非常简洁,下面这个脚本会自动检查环境并完成初始化配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function Assert-DotNetSdk { $sdk = dotnet --version 2 >$null if (-not $sdk ) { Write-Host "正在安装 .NET SDK ..." -ForegroundColor Yellow winget install Microsoft.DotNet.SDK.8 --accept-source-agreements } else { Write-Host ".NET SDK 版本: $sdk " -ForegroundColor Green } dotnet tool install -g Microsoft.dotnet-interactive dotnet interactive jupyter install $kernels = jupyter kernelspec list 2 >$null if ($kernels -match "powershell" ) { Write-Host "PowerShell 内核已注册" -ForegroundColor Green } else { Write-Host "PowerShell 内核未找到,请手动执行 jupyter kernelspec list 排查" -ForegroundColor Red } } Assert-DotNetSdk
执行结果示例:
1 2 .NET SDK 版本: 8.0.403 PowerShell 内核已注册
安装完成后,在 VS Code 中安装 Polyglot Notebooks 扩展(扩展 ID: ms-dotnettools.dotnet-interactive-vscode),然后创建一个 .dib 或 .ipynb 文件即可开始使用。Polyglot Notebooks 提供了一组称为 “Magic Commands” 的特殊指令,以 #! 开头,用于控制笔记本行为。以下是最常用的几个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $PSVersionTable [System.Environment ]::OSVersion
执行结果示例:
1 2 3 PowerShell 7.4.6 OS: Microsoft Windows 10.0.22631 Platform: Win32NT
Magic Commands 的强大之处在于它们可以在单元格的第一行使用,无需额外的配置。这意味着你可以逐个单元格地指定语言,实现真正的多语言混合编程。
多语言协作与变量共享 Polyglot Notebooks 最令人兴奋的特性是跨语言变量共享。你可以在 PowerShell 中采集数据,传递给 C# 进行复杂计算,再用 JavaScript 渲染为可交互的 HTML 页面。下面这个示例演示了完整的多语言协作流程。
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 $cpuUsage = [math ]::Round( (Get-CimInstance Win32_Processor).LoadPercentage, 2 ) $os = Get-CimInstance Win32_OperatingSystem$totalMemGB = [math ]::Round($os .TotalVisibleMemorySize / 1 MB, 2 )$freeMemGB = [math ]::Round($os .FreePhysicalMemory / 1 MB, 2 )$usedMemGB = [math ]::Round($totalMemGB - $freeMemGB , 2 )$memPercent = [math ]::Round(($usedMemGB / $totalMemGB ) * 100 , 1 )$disks = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Select-Object DeviceID, @ {N="TotalGB" ; E={[math ]::Round($_ .Size/1 GB,2 )}}, @ {N="FreeGB" ; E={[math ]::Round($_ .FreeSpace/1 GB,2 )}}, @ {N="UsedPercent" ; E={[math ]::Round(($_ .Size - $_ .FreeSpace)/$_ .Size * 100 , 1 )}} $systemInfo = @ { HostName = $env:COMPUTERNAME CpuUsage = $cpuUsage TotalMemGB = $totalMemGB UsedMemGB = $usedMemGB FreeMemGB = $freeMemGB MemPercent = $memPercent Disks = $disks Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" } Write-Host "系统数据采集完成: $ ($systemInfo .HostName)" -ForegroundColor Cyan$systemInfo | ConvertTo-Json -Depth 3
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 系统数据采集完成: DESKTOP-WIN11 { "HostName": "DESKTOP-WIN11", "CpuUsage": 23, "TotalMemGB": 31.87, "UsedMemGB": 18.54, "FreeMemGB": 13.33, "MemPercent": 58.2, "Disks": [ { "DeviceID": "C:", "TotalGB": 476.37, "FreeGB": 198.24, "UsedPercent": 58.4 }, { "DeviceID": "D:", "TotalGB": 931.51, "FreeGB": 645.12, "UsedPercent": 30.7 } ], "Timestamp": "2026-01-08 09:15:32" }
接下来在 C# 单元格中接收这些数据并进行处理分析:
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 using System.Text.Json;// 解析 PowerShell 传入的 JSON 数据 var json = systemInfo.ToString(); var doc = JsonDocument.Parse(json); var root = doc.RootElement; // 提取关键指标 var hostName = root.GetProperty("HostName" ).GetString(); var cpuUsage = root.GetProperty("CpuUsage" ).GetInt32(); var memPercent = root.GetProperty("MemPercent" ).GetDouble(); var timestamp = root.GetProperty("Timestamp" ).GetString(); // 生成健康评估报告 var healthScore = 100.0 ; var warnings = new List<string>(); if (cpuUsage > 80 ) { healthScore -= 30 ; warnings.Add($ "CPU 使用率过高: {cpuUsage}%" ); } else if (cpuUsage > 60 ) { healthScore -= 10 ; warnings.Add($ "CPU 使用率偏高: {cpuUsage}%" ); } if (memPercent > 85 ) { healthScore -= 25 ; warnings.Add($ "内存使用率过高: {memPercent}%" ); } else if (memPercent > 70 ) { healthScore -= 10 ; warnings.Add($ "内存使用率偏高: {memPercent}%" ); } var status = healthScore >= 80 ? "健康" : healthScore >= 60 ? "注意" : "警告" ; // 将结果保存为共享变量供后续使用 var report = new { HostName = hostName, Timestamp = timestamp, HealthScore = Math.Round(healthScore, 1 ), Status = status, Warnings = warnings, Summary = $ "[{status}] {hostName} 健康评分: {healthScore:F1}/100" }; Console.WriteLine(report.Summary); if (warnings.Any()) { Console.WriteLine("告警项:" ); warnings.ForEach(w => Console.WriteLine($ " - {w}" )); }
执行结果示例:
1 2 3 [注意] DESKTOP-WIN11 健康评分: 80.0/100 告警项: - 内存使用率偏高: 58.2%
通过 #!set 命令,C# 单元格直接读取了 PowerShell 中定义的 $systemInfo 变量。这种跨语言变量传递是 Polyglot Notebooks 的核心能力,使得每种语言都可以专注于自己最擅长的领域。
数据分析与可视化实战 最后一个场景是数据分析的完整工作流:用 PowerShell 导入 CSV 数据并进行清洗,然后用 C# 进行聚合计算,最后生成 HTML 可视化报告。
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 $sampleData = @ ( @ { Server="WEB-01" ; Region="East" ; CPU=45 ; Memory=62 ; Requests=12500 ; Errors=23 ; Date="2026-01-01" } @ { Server="WEB-02" ; Region="East" ; CPU=78 ; Memory=81 ; Requests=18900 ; Errors=156 ; Date="2026-01-01" } @ { Server="API-01" ; Region="West" ; CPU=32 ; Memory=55 ; Requests=45000 ; Errors=12 ; Date="2026-01-01" } @ { Server="API-02" ; Region="West" ; CPU=91 ; Memory=88 ; Requests=52000 ; Errors=423 ; Date="2026-01-01" } @ { Server="DB-01" ; Region="Central" ; CPU=67 ; Memory=74 ; Requests=8500 ; Errors=5 ; Date="2026-01-01" } @ { Server="DB-02" ; Region="Central" ; CPU=55 ; Memory=69 ; Requests=7200 ; Errors=2 ; Date="2026-01-02" } @ { Server="WEB-01" ; Region="East" ; CPU=52 ; Memory=65 ; Requests=13100 ; Errors=31 ; Date="2026-01-02" } @ { Server="API-01" ; Region="West" ; CPU=38 ; Memory=58 ; Requests=47200 ; Errors=18 ; Date="2026-01-02" } @ { Server="CACHE-01" ; Region="East" ; CPU=22 ; Memory=91 ; Requests=98000 ; Errors=1 ; Date="2026-01-02" } @ { Server="WORKER-01" ; Region="West" ; CPU=85 ; Memory=43 ; Requests=3200 ; Errors=8 ; Date="2026-01-02" } ) $records = $sampleData | ForEach-Object { [PSCustomObject ]@ { Server = $_ .Server Region = $_ .Region CPU = [int ]$_ .CPU Memory = [int ]$_ .Memory Requests = [int ]$_ .Requests Errors = [int ]$_ .Errors Date = $_ .Date ErrorRate = [math ]::Round($_ .Errors / $_ .Requests * 100 , 3 ) } } $cleanData = $records | ForEach-Object { $flags = @ () if ($_ .CPU -gt 80 ) { $flags += "CPU高负载" } if ($_ .Memory -gt 85 ) { $flags += "内存高占用" } if ($_ .ErrorRate -gt 0.5 ) { $flags += "错误率异常" } $_ | Add-Member -NotePropertyName "Flags" -NotePropertyValue ($flags -join ", " ) -PassThru } $totalRecords = $cleanData .Count$flaggedCount = ($cleanData | Where-Object { $_ .Flags }).Count$avgCpu = [math ]::Round(($cleanData | Measure-Object -Property CPU -Average ).Average, 1 )$avgMem = [math ]::Round(($cleanData | Measure-Object -Property Memory -Average ).Average, 1 )Write-Host "数据概览:" -ForegroundColor CyanWrite-Host " 总记录数: $totalRecords " Write-Host " 异常标记数: $flaggedCount " Write-Host " 平均 CPU: $ {avgCpu}%" Write-Host " 平均内存: $ {avgMem}%" Write-Host "" $regionSummary = $cleanData | Group-Object Region | ForEach-Object { $group = $_ .Group [PSCustomObject ]@ { Region = $_ .Name Count = $_ .Count AvgCPU = [math ]::Round(($group | Measure-Object CPU -Average ).Average, 1 ) AvgMemory = [math ]::Round(($group | Measure-Object Memory -Average ).Average, 1 ) TotalReqs = ($group | Measure-Object Requests -Sum ).Sum TotalErrors = ($group | Measure-Object Errors -Sum ).Sum } } $regionSummary | Format-Table -AutoSize $htmlReport = @" <!DOCTYPE html> <html> <head><meta charset="utf-8"><title>服务器监控报告</title> <style> body { font-family: 'Segoe UI', sans-serif; margin: 20px; background: #f5f5f5; } h1 { color: #0078d4; } table { border-collapse: collapse; width: 100%; margin-top: 10px; } th { background: #0078d4; color: white; padding: 10px; text-align: left; } td { padding: 8px 10px; border-bottom: 1px solid #ddd; } tr:nth-child(even) { background: #f9f9f9; } .flag { color: #d13438; font-weight: bold; } .ok { color: #107c10; } .warn { color: #ff8c00; } </style> </head> <body> <h1>服务器监控报告</h1> <p>生成时间: $ (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</p> <h2>异常服务器列表</h2> <table> <tr><th>服务器</th><th>区域</th><th>CPU</th><th>内存</th> <th>请求数</th><th>错误数</th><th>错误率</th><th>标记</th></tr> "@ foreach ($row in ($cleanData | Where-Object { $_ .Flags })) { $cpuClass = if ($row .CPU -gt 80 ) { "flag" } elseif ($row .CPU -gt 60 ) { "warn" } else { "ok" } $memClass = if ($row .Memory -gt 85 ) { "flag" } elseif ($row .Memory -gt 70 ) { "warn" } else { "ok" } $htmlReport += @" <tr> <td>$ ($row .Server)</td> <td>$ ($row .Region)</td> <td class="$cpuClass ">$ ($row .CPU)%</td> <td class="$memClass ">$ ($row .Memory)%</td> <td>$ ($row .Requests)</td> <td>$ ($row .Errors)</td> <td>$ ($row .ErrorRate)%</td> <td class="flag">$ ($row .Flags)</td> </tr> "@ } $htmlReport += "</table></body></html>" $reportPath = Join-Path $env:TEMP "ServerMonitor-$ (Get-Date -Format 'yyyyMMdd-HHmmss').html" $htmlReport | Out-File -FilePath $reportPath -Encoding utf8Write-Host "" Write-Host "HTML 报告已生成: $reportPath " -ForegroundColor Green
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 数据概览: 总记录数: 10 异常标记数: 4 平均 CPU: 56.5% 平均内存: 68.6% Region Count AvgCPU AvgMemory TotalReqs TotalErrors ------ ----- ------ --------- --------- ----------- Central 2 61.0 71.5 15700 7 East 4 49.2 74.8 132500 211 West 4 61.5 61.8 104400 461 HTML 报告已生成: /tmp/ServerMonitor-20260108-091532.html
这个实战案例展示了 Polyglot Notebooks 在数据运维中的完整工作流:PowerShell 负责数据采集和清洗,通过对象管道完成分组聚合,最终生成带有条件高亮的 HTML 可视化报告。在实际工作中,你可以将 Import-Csv 替换为真实数据源,还可以添加 C# 单元格进行更复杂的统计分析。
注意事项
Polyglot Notebooks 需要 .NET 8.0 SDK 或更高版本。安装前请确认系统已满足此先决条件,否则 dotnet interactive 命令将无法正常注册 Jupyter 内核。
跨语言变量共享通过 #!set 命令实现,传递时数据会被序列化为 JSON 格式。复杂对象(如 PSCustomObject)可以顺利传递,但包含方法或闭包的对象会被自动剥离,只保留属性数据。
在 VS Code 中使用 .dib 格式是 Polyglot Notebooks 的原生体验,功能最为完整。如果使用标准 .ipynb 格式,请确保在笔记本元数据中正确指定了内核为 .NET (Polyglot)。
Magic Commands 必须写在单元格的第一行,前面不能有任何其他代码或空行。否则笔记本引擎无法识别指令,会将其作为普通文本处理。
当笔记本中的数据量较大时(例如数万行 CSV),建议在 PowerShell 单元格中使用 Measure-Object、Group-Object 等流式处理命令,避免将全量数据加载到内存中导致性能下降。
Polyglot Notebooks 目前不支持直接在 JupyterLab 中运行 PowerShell 内核的自动补全功能。如果需要 IntelliSense 支持,推荐在 VS Code 中配合 Polyglot 扩展使用,可以获得完整的代码补全和语法检查体验。