igozhang

——

    Windows 常用 PowerShell 命令

    适用于 Windows 10 及以上
    所有命令均在 10 秒内完成。


    快速开始

    打开 PowerShell,复制对应章节的命令块,整段粘贴回车即可运行。

    各命令块第一行为可调参数,按需修改后再执行:

    命令 可调参数 示例 耗时
    wininfo 直接运行 ~3s
    bigfiles $MinMB$TopN$TimeoutSec $MinMB = 500; $TopN = 10 ≤10s
    topprocs $TopN $TopN = 10 ~1s
    netinfo 直接运行 ~1s
    netio $TopN$IoSec $TopN = 8; $IoSec = 3 ~3s

    命令一览

    # 命令 功能 默认参数
    1 wininfo 系统概况(硬件/CPU/GPU/内存/磁盘/电池)
    2 bigfiles 大文件扫描 MinMB=100, TopN=25, TimeoutSec=10
    3 topprocs CPU / 内存占用 Top N TopN=5
    4 netinfo 网络信息(网卡/IP/DNS/VPN/路由/网关)
    5 netio 网络 & 磁盘 IO Top N TopN=5, IoSec=2

    1. 系统概况 wininfo

    一条命令汇总 Windows 版本、硬件、CPU/GPU、内存、磁盘、电池与运行时长。纯 WMI/CIM 查询,约 3 秒。

    命令

    Write-Host '========== Windows 系统概况 =========='
    $os = Get-CimInstance Win32_OperatingSystem
    $cs = Get-CimInstance Win32_ComputerSystem
    $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
    $gpus = Get-CimInstance Win32_VideoController | Where-Object { $_.Name -and $_.Name -notmatch 'Microsoft Basic' }
    
    function fb([long]$b) {
        $u = 'B','KB','MB','GB','TB'; $i = 0; $v = [double]$b
        while ($v -ge 1024 -and $i -lt 4) { $v /= 1024; $i++ }
        '{0,7:N1} {1,-2}' -f $v, $u[$i]
    }
    
    Write-Host '[系统版本]'
    Write-Host ('  {0} {1} (Build {2})' -f $os.Caption.Trim(), $os.OSArchitecture, $os.BuildNumber)
    $installDate = try { [Management.ManagementDateTimeConverter]::ToDateTime($os.InstallDate) } catch { $null }
    Write-Host ('  版本号 {0}  安装日期 {1}' -f $os.Version, $(if ($installDate) { $installDate.ToString('yyyy-MM-dd') } else { '未知' }))
    
    Write-Host "`n[硬件信息]"
    Write-Host ('  制造商/型号  {0} / {1}' -f $cs.Manufacturer, $cs.Model)
    Write-Host ('  序列号       {0}' -f (Get-CimInstance Win32_BIOS).SerialNumber)
    Write-Host ('  域/工作组    {0}' -f $(if ($cs.PartOfDomain) { $cs.Domain } else { $cs.Workgroup }))
    
    Write-Host "`n[CPU]"
    $cores = (Get-CimInstance Win32_Processor | Measure-Object NumberOfCores -Sum).Sum
    $logical = (Get-CimInstance Win32_Processor | Measure-Object NumberOfLogicalProcessors -Sum).Sum
    Write-Host ('  {0}' -f $cpu.Name.Trim())
    Write-Host ('  物理/逻辑核心  {0} / {1}  当前负载 {2}%' -f $cores, $logical, $cpu.LoadPercentage)
    
    Write-Host "`n[GPU]"
    if ($gpus) {
        foreach ($g in $gpus) {
            $vram = if ($g.AdapterRAM -and $g.AdapterRAM -gt 0) { '{0:N0} MB' -f ($g.AdapterRAM / 1MB) } else { '未知' }
            Write-Host ('  {0}  显存 {1}  驱动 {2}' -f $g.Name, $vram, $g.DriverVersion)
        }
    } else { Write-Host '  (未检测到独立/专用显卡信息)' }
    
    Write-Host "`n[内存]"
    $totalRam = [math]::Round($cs.TotalPhysicalMemory / 1GB, 1)
    $freeRam  = [math]::Round($os.FreePhysicalMemory / 1MB, 1)
    $usedRam  = [math]::Round($totalRam - ($os.FreePhysicalMemory / 1MB), 1)
    $usedPct  = if ($totalRam) { [math]::Round($usedRam / $totalRam * 100, 1) } else { 0 }
    Write-Host ('  总量 {0} GB | 已用 {1} GB ({2}%) | 可用 {3} GB' -f $totalRam, $usedRam, $usedPct, $freeRam)
    
    Write-Host "`n[磁盘]"
    Get-CimInstance Win32_LogicalDisk -Filter 'DriveType=3' | ForEach-Object {
        $total = $_.Size; $free = $_.FreeSpace; $used = $total - $free
        $pct = if ($total) { [math]::Round($used / $total * 100, 1) } else { 0 }
        Write-Host ('  {0}  总 {1} | 已用 {2} ({3}%) | 可用 {4}  [{5}]' -f $_.DeviceID, (fb $total), (fb $used), $pct, (fb $free), $_.VolumeName)
    }
    
    Write-Host "`n[电池]"
    $bat = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue
    if ($bat) {
        foreach ($b in $bat) {
            $status = switch ($b.BatteryStatus) { 1 {'放电中'} 2 {'已连接电源'} 3 {'已充满'} default {"状态码 $($b.BatteryStatus)"} }
            Write-Host ('  电量 {0}%  {1}  预估剩余 {2} 分钟' -f $b.EstimatedChargeRemaining, $status, $b.EstimatedRunTime)
        }
    } else { Write-Host '  (台式机或未检测到电池)' }
    
    Write-Host "`n[运行时长]"
    $up = (Get-Date) - $os.LastBootUpTime
    Write-Host ('  已运行 {0} 天 {1:D2}:{2:D2}:{3:D2}  (上次启动 {4:yyyy-MM-dd HH:mm:ss})' -f $up.Days, $up.Hours, $up.Minutes, $up.Seconds, $os.LastBootUpTime)
    Write-Host '======================================'
    

    说明

    • 磁盘:仅列出固定磁盘(DriveType=3),不含 U 盘/网络盘。
    • 电池:台式机或无电池设备会显示「未检测到电池」。
    • GPU 显存:部分集成显卡 WMI 返回值不准确,显示「未知」属正常。

    2. 大文件扫描 bigfiles

    10 秒内列出实际占用最大的 25 个文件(默认 >100MB)。核心用 Windows 搜索索引(类似 macOS mdfind),并对 Downloads / Desktop / Temp 等关键目录做补漏;索引结果不足时,对 Program Files 限时后台扫描。

    只需改第一行 $MinMB / $TopN / $TimeoutSec 即可(单位 MB,如 1005001024):

    $MinMB = 100; $TopN = 25; $TimeoutSec = 10
    $minBytes = [long]$MinMB * 1MB
    $deadline = (Get-Date).AddSeconds($TimeoutSec)
    $sw = [Diagnostics.Stopwatch]::StartNew()
    $seen = @{}; $files = [System.Collections.Generic.List[object]]::new()
    $user = [regex]::Escape($env:USERNAME)
    
    function fb([long]$b) {
        $u = 'B','KB','MB','GB','TB'; $i = 0; $v = [double]$b
        while ($v -ge 1024 -and $i -lt 4) { $v /= 1024; $i++ }
        '{0,7:N1} {1,-2}' -f $v, $u[$i]
    }
    function Is-Allowed([string]$path) {
        if (-not $path) { return $false }
        if ($path.StartsWith($env:USERPROFILE, [StringComparison]::OrdinalIgnoreCase)) { return $true }
        if ($path -match '(?i)\\Program Files( \(x86\))?\\') { return $true }
        if ($path -match "\\$user\\") { return $true }
        return $false
    }
    function Add-Entry($path, $size) {
        if (-not $path -or $size -lt $minBytes -or -not (Is-Allowed $path)) { return }
        try {
            $fi = [IO.FileInfo]::new($path)
            if (-not $fi.Exists) { return }
            $path = $fi.FullName
        } catch { return }
        $dedupeKey = "$size|$($fi.Name.ToLower())"
        if ($seen.ContainsKey($dedupeKey)) { return }
        $seen[$dedupeKey] = $true
        $files.Add([PSCustomObject]@{ FullName = $path; Length = $size })
    }
    
    Write-Host "========== 大文件扫描 (>$MinMB MB, Top $TopN, ${TimeoutSec}s 内) =========="
    
    # 1) Windows 搜索索引(毫秒级,类似 mdfind)
    $indexOk = $false
    try {
        $conn = New-Object -ComObject ADODB.Connection
        $conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';")
        $rs = $conn.Execute("SELECT TOP 500 System.ItemPathDisplay, System.Size FROM SystemIndex WHERE System.Size > $minBytes ORDER BY System.Size DESC")
        while (-not $rs.EOF) {
            Add-Entry $rs.Fields.Item('System.ItemPathDisplay').Value ([long]$rs.Fields.Item('System.Size').Value)
            $rs.MoveNext()
        }
        $conn.Close(); $indexOk = $true
    } catch {}
    
    Write-Host ('[扫描方式] 索引{0} + 用户目录补漏 + Program Files 限时扫描' -f $(if ($indexOk) { 'OK' } else { '不可用' }))
    
    # 2) 关键用户目录补漏(覆盖尚未入索引的新文件)
    foreach ($root in @("$env:USERPROFILE\Downloads", "$env:USERPROFILE\Desktop", "$env:USERPROFILE\Videos", "$env:LOCALAPPDATA\Temp")) {
        if ((Get-Date) -ge $deadline) { break }
        if (-not (Test-Path -LiteralPath $root)) { continue }
        try {
            foreach ($f in [IO.Directory]::EnumerateFiles($root, '*', [IO.SearchOption]::AllDirectories)) {
                if ((Get-Date) -ge $deadline) { break }
                try {
                    $fi = [IO.FileInfo]::new($f)
                    if ($fi.Exists) { Add-Entry $fi.FullName $fi.Length }
                } catch {}
            }
        } catch {}
    }
    
    # 3) Program Files 限时后台扫描(索引未覆盖时补全)
    $left = [math]::Max(1, [int]($deadline - (Get-Date)).TotalSeconds)
    if (($files.Count -lt $TopN) -and $left -gt 0) {
        $job = Start-Job -ArgumentList $minBytes, @('C:\Program Files', 'C:\Program Files (x86)') -ScriptBlock {
            param($min, $roots)
            $out = [System.Collections.Generic.List[object]]::new()
            foreach ($root in $roots) {
                if (-not (Test-Path -LiteralPath $root)) { continue }
                try {
                    foreach ($f in [IO.Directory]::EnumerateFiles($root, '*', [IO.SearchOption]::AllDirectories)) {
                        try {
                            $fi = [IO.FileInfo]::new($f)
                            if ($fi.Length -ge $min) { $out.Add([PSCustomObject]@{ FullName = $fi.FullName; Length = $fi.Length }) }
                        } catch {}
                    }
                } catch {}
            }
            return $out
        }
        $null = Wait-Job $job -Timeout $left
        if ($job.State -eq 'Completed') {
            Receive-Job $job | ForEach-Object { Add-Entry $_.FullName $_.Length }
        } else { Stop-Job $job -ErrorAction SilentlyContinue }
        Remove-Job $job -Force -ErrorAction SilentlyContinue
    }
    
    Write-Host ('[耗时] {0:N1}s  [命中] {1} 个' -f ($sw.ElapsedMilliseconds / 1000), $files.Count)
    Write-Host "[Top $TopN 文件]"
    $files | Sort-Object Length -Descending | Select-Object -First $TopN | ForEach-Object {
        Write-Host ('  {0}  {1}' -f (fb $_.Length), $_.FullName)
    }
    Write-Host '========================================='
    

    说明

    • 索引:依赖 Windows Search 服务;若索引未开启,会自动降级为目录扫描(仍受 $TimeoutSec 限制)。
    • 范围:当前用户目录 + Program Files;其他用户/盘符仅当索引命中时显示。
    • 超时:硬限制 $TimeoutSec(默认 10s),Program Files 扫描超时自动终止。
    • 路径别名:中文 Windows 下 C:\UsersC:\用户 为同一目录,Get-Item 可能报错;已改用 [IO.FileInfo] 静默验证,同文件按「大小+文件名」去重。
    • 大小:按文件逻辑大小排序,非 NTFS 压缩后的物理占用。

    3. 进程占用 Top N topprocs

    快照式列出 CPU / 内存占用最高的进程(默认 Top 5),含系统整体 CPU 与内存概览。实测约 1 秒。

    只需改第一行 $TopN 即可:

    $TopN = 5
    
    function fb([long]$b) {
        $u = 'B','KB','MB','GB','TB'; $i = 0; $v = [double]$b
        while ($v -ge 1024 -and $i -lt 4) { $v /= 1024; $i++ }
        '{0,7:N1} {1,-2}' -f $v, $u[$i]
    }
    
    Write-Host "========== 进程占用 Top $TopN =========="
    $os = Get-CimInstance Win32_OperatingSystem
    $totalRam = $os.TotalVisibleMemorySize * 1KB
    $usedRam  = $totalRam - $os.FreePhysicalMemory * 1KB
    $cpuLoad  = (Get-CimInstance Win32_Processor | Measure-Object LoadPercentage -Average).Average
    
    Write-Host '[系统概览]'
    Write-Host ('  CPU 平均负载 {0:N1}%' -f $cpuLoad)
    Write-Host ('  物理内存  已用 {0} / {1} ({2:N1}%)' -f (fb $usedRam), (fb $totalRam), ($usedRam / $totalRam * 100))
    
    $p1 = Get-Process | Where-Object { $_.Id -gt 0 } | ForEach-Object {
        [PSCustomObject]@{ Id = $_.Id; Name = $_.ProcessName; Cpu = $_.CPU }
    }
    Start-Sleep -Milliseconds 500
    $cpuMap = @{}; $p1 | ForEach-Object { $cpuMap[$_.Id] = $_ }
    
    $rows = foreach ($b in (Get-Process | Where-Object { $_.Id -gt 0 })) {
        if (-not $cpuMap.ContainsKey($b.Id)) { continue }
        $a = $cpuMap[$b.Id]
        $cpuPct = [math]::Round([math]::Max(0, $b.CPU - $a.Cpu) / 0.5 * 100, 1)
        [PSCustomObject]@{
            Id = $b.Id; Name = $b.ProcessName
            CpuPct = $cpuPct
            MemPct = [math]::Round($b.WorkingSet64 / $totalRam * 100, 1)
            MemMB  = [math]::Round($b.WorkingSet64 / 1MB, 0)
        }
    }
    
    Write-Host "`n[CPU Top $TopN]"
    $rows | Sort-Object CpuPct -Descending | Select-Object -First $TopN | ForEach-Object {
        $n = if ($_.Name.Length -gt 36) { $_.Name.Substring(0, 33) + '...' } else { $_.Name }
        Write-Host ('  {0,5:N1}% CPU  {1,5:N1}% MEM  {2,6:N0} MB  [{3}] {4}' -f $_.CpuPct, $_.MemPct, $_.MemMB, $_.Id, $n)
    }
    
    Write-Host "`n[内存 Top $TopN]"
    $rows | Sort-Object MemMB -Descending | Select-Object -First $TopN | ForEach-Object {
        $n = if ($_.Name.Length -gt 36) { $_.Name.Substring(0, 33) + '...' } else { $_.Name }
        Write-Host ('  {0,5:N1}% CPU  {1,5:N1}% MEM  {2,6:N0} MB  [{3}] {4}' -f $_.CpuPct, $_.MemPct, $_.MemMB, $_.Id, $n)
    }
    Write-Host '========================================='
    

    说明

    • CPU %:多核机器上单进程可超过 100%(如 16 逻辑核满载约 1600%),属正常现象。
    • MEM % / MB%MEM 占总内存比例,MB 为 Working Set 常驻物理内存。
    • 快照:0.5 秒双采样估算 CPU,非任务管理器那样的持续平均值;多次运行结果会有波动。

    4. 网络信息 netinfo

    汇总默认网关、网卡/IP、DNS、VPN/隧道、路由表等。依赖 Windows 自带 NetTCPIP 模块,通常 < 1 秒。

    Write-Host '========== 网络信息 =========='
    
    Write-Host '[默认路由]'
    $def = Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction SilentlyContinue |
        Sort-Object RouteMetric | Select-Object -First 1
    if ($def) {
        $ifAlias = (Get-NetIPInterface -InterfaceIndex $def.InterfaceIndex -ErrorAction SilentlyContinue).InterfaceAlias
        Write-Host ('  网关 {0}  接口 {1} (ifIndex {2})' -f $def.NextHop, $ifAlias, $def.InterfaceIndex)
    } else { Write-Host '  (未找到默认 IPv4 路由)' }
    
    Write-Host "`n[主接口 / IP 配置]"
    Get-NetIPConfiguration | Where-Object { $_.IPv4DefaultGateway } | ForEach-Object {
        Write-Host ('  {0,-20} IPv4 {1}  网关 {2}' -f $_.InterfaceAlias, $_.IPv4Address.IPAddress, $_.IPv4DefaultGateway.NextHop)
        if ($_.IPv6Address) {
            $_.IPv6Address.IPAddress | Where-Object { $_ -notmatch '^fe80:' -and $_ -ne '::1' } | ForEach-Object {
                Write-Host ('  {0,-20} IPv6 {1}' -f $_.InterfaceAlias, $_)
            }
        }
    }
    
    Write-Host "`n[网卡与 IP]"
    Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | ForEach-Object {
        $adapter = $_
        Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -ErrorAction SilentlyContinue |
            Where-Object { $_.IPAddress -ne '127.0.0.1' -and $_.IPAddress -ne '::1' -and $_.PrefixOrigin -ne 'WellKnown' } |
            ForEach-Object {
                Write-Host ('  {0,-20} {1} {2}/{3}  MAC {4}' -f $adapter.Name, $_.AddressFamily, $_.IPAddress, $_.PrefixLength, $adapter.MacAddress)
            }
    }
    
    Write-Host "`n[DNS]"
    Get-DnsClientServerAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue |
        Where-Object { $_.ServerAddresses } |
        ForEach-Object {
            Write-Host ('  {0,-20} {1}' -f $_.InterfaceAlias, ($_.ServerAddresses -join ', '))
        }
    
    Write-Host "`n[VPN / 隧道]"
    $vpn = Get-VpnConnection -ErrorAction SilentlyContinue
    if ($vpn) {
        $vpn | ForEach-Object { Write-Host ('  {0,-20} {1}' -f $_.Name, $_.ConnectionStatus) }
    } else { Write-Host '  (未配置或未检测到 VPN 连接)' }
    Get-NetAdapter | Where-Object { $_.InterfaceDescription -match 'WAN Miniport|VPN|TAP|TUN|WireGuard|OpenVPN' } | ForEach-Object {
        Write-Host ('  适配器 {0,-16} {1}  {2}' -f $_.Name, $_.Status, $_.InterfaceDescription)
    }
    
    Write-Host "`n[路由表 (IPv4 摘要)]"
    Get-NetRoute -AddressFamily IPv4 -ErrorAction SilentlyContinue |
        Where-Object { $_.DestinationPrefix -eq '0.0.0.0/0' -or $_.InterfaceAlias -match 'VPN|TAP|TUN|WireGuard' } |
        Sort-Object RouteMetric | Select-Object -First 12 |
        ForEach-Object {
            Write-Host ('  {0,-18} via {1,-15} metric {2,3}  {3}' -f $_.DestinationPrefix, $_.NextHop, $_.RouteMetric, $_.InterfaceAlias)
        }
    Write-Host '=================================='
    

    说明

    • 主接口Get-NetIPConfiguration 中带默认网关的接口为当前可用于上网的候选接口。
    • VPN 适配器:即使 VPN 显示 Disconnected,虚拟网卡驱动(如 Sangfor、WireGuard)仍可能存在于适配器列表中。
    • 路由表:仅展示默认路由与 VPN/隧道相关条目;完整路由用 Get-NetRouteroute print

    5. 网络 & 磁盘 IO Top N netio

    统计系统网络/磁盘 IO 概况,并列出 Top N 网卡/进程(默认 5)。网络与磁盘同一段采样窗口,改 $TopN / $IoSec 即可。

    $TopN = 5; $IoSec = 2
    
    function fb([long]$b) {
        $u = 'B','KB','MB','GB','TB'; $i = 0; $v = [double]$b
        while ($v -ge 1024 -and $i -lt 4) { $v /= 1024; $i++ }
        '{0,7:N1} {1,-2}' -f $v, $u[$i]
    }
    
    Write-Host "========== 网络 & 磁盘 IO Top $TopN =========="
    
    Write-Host '[系统概览]'
    $disk = Get-Counter '\PhysicalDisk(_Total)\Disk Read Bytes/sec', '\PhysicalDisk(_Total)\Disk Write Bytes/sec' -ErrorAction SilentlyContinue
    if ($disk) {
        $rd = ($disk.CounterSamples | Where-Object { $_.Path -like '*Read*' }).CookedValue
        $wr = ($disk.CounterSamples | Where-Object { $_.Path -like '*Write*' }).CookedValue
        Write-Host ('  磁盘瞬时速率  读 {0}/s  写 {1}/s' -f (fb $rd), (fb $wr))
    }
    
    # 同一段采样窗口:网卡 + 磁盘进程计数器
    $s1 = Get-NetAdapterStatistics -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch 'Loopback' }
    $d1r = Get-Counter '\Process(*)\IO Read Bytes/sec' -ErrorAction SilentlyContinue
    $d1w = Get-Counter '\Process(*)\IO Write Bytes/sec' -ErrorAction SilentlyContinue
    Start-Sleep -Seconds $IoSec
    $s2 = Get-NetAdapterStatistics -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch 'Loopback' }
    $d2r = Get-Counter '\Process(*)\IO Read Bytes/sec' -ErrorAction SilentlyContinue
    $d2w = Get-Counter '\Process(*)\IO Write Bytes/sec' -ErrorAction SilentlyContinue
    
    Write-Host "`n[网络 IO Top $TopN 网卡 (${IoSec}s 采样)]"
    Write-Host '  (Windows 标准计数器无进程级网络吞吐,此处按网卡统计)'
    if ($s1 -and $s2) {
        $map = @{}; $s1 | ForEach-Object { $map[$_.Name] = $_ }
        $netRows = foreach ($b in $s2) {
            if (-not $map.ContainsKey($b.Name)) { continue }
            $a = $map[$b.Name]
            $rin = [math]::Max(0, ($b.ReceivedBytes - $a.ReceivedBytes) / $IoSec)
            $out = [math]::Max(0, ($b.SentBytes - $a.SentBytes) / $IoSec)
            $tot = $rin + $out
            if ($tot -le 0) { continue }
            [PSCustomObject]@{ Name = $b.Name; Total = $tot; In = $rin; Out = $out }
        }
        $netRows | Sort-Object Total -Descending | Select-Object -First $TopN | ForEach-Object {
            Write-Host ('  总 {0}/s  (↓{1}/s ↑{2}/s)  {3}' -f (fb $_.Total), (fb $_.In), (fb $_.Out), $_.Name)
        }
    } else { Write-Host '  (无法读取网卡统计)' }
    
    Write-Host "`n[磁盘 IO Top $TopN 进程 (${IoSec}s 采样)]"
    if ($d1r -and $d2r -and $d1w -and $d2w) {
        $readMap = @{}; $writeMap = @{}
        $d1r.CounterSamples | ForEach-Object { $readMap[$_.InstanceName] = $_.CookedValue }
        $d1w.CounterSamples | ForEach-Object { $writeMap[$_.InstanceName] = $_.CookedValue }
    
        $diskRows = @{}
        foreach ($s in $d2r.CounterSamples) {
            $inst = $s.InstanceName; $name = ($inst -replace '#\d+$', '')
            if ($name -in '_Total', 'Idle', '_Global_') { continue }
            if (-not $readMap.ContainsKey($inst)) { continue }
            $r = [math]::Max(0, ($s.CookedValue - $readMap[$inst]) / $IoSec)
            if (-not $diskRows.ContainsKey($name)) { $diskRows[$name] = @{ R = 0; W = 0 } }
            $diskRows[$name].R += $r
        }
        foreach ($s in $d2w.CounterSamples) {
            $inst = $s.InstanceName; $name = ($inst -replace '#\d+$', '')
            if ($name -in '_Total', 'Idle', '_Global_') { continue }
            if (-not $writeMap.ContainsKey($inst)) { continue }
            $w = [math]::Max(0, ($s.CookedValue - $writeMap[$inst]) / $IoSec)
            if (-not $diskRows.ContainsKey($name)) { $diskRows[$name] = @{ R = 0; W = 0 } }
            $diskRows[$name].W += $w
        }
    
        $diskRows.GetEnumerator() | ForEach-Object {
            [PSCustomObject]@{ Name = $_.Key; Total = $_.Value.R + $_.Value.W; Read = $_.Value.R; Write = $_.Value.W }
        } | Where-Object { $_.Total -gt 0 } | Sort-Object Total -Descending | Select-Object -First $TopN | ForEach-Object {
            Write-Host ('  总 {0}/s  (读 {1}/s 写 {2}/s)  {3}' -f (fb $_.Total), (fb $_.Read), (fb $_.Write), $_.Name)
        }
    } else { Write-Host '  (性能计数器不可用,请以管理员身份重试)' }
    Write-Host '========================================='
    

    说明

    • 网络 IO:Windows 标准性能计数器不提供进程级网络吞吐(macOS 的 nettop 才有),此处按网卡两次采样计算 ↓接收 ↑发送 速率。
    • 磁盘系统\PhysicalDisk(_Total)\ 为整盘瞬时速率。
    • 磁盘进程\Process(*)\IO Read/Write Bytes/sec 两次采样;同名多实例进程(如 chrome#1chrome#2)会合并。
    • 耗时:约 $IoSec + 1 秒(默认约 3 秒),网络与磁盘共用同一段采样窗口。

    Mac vs Windows 对照

    功能 Mac (macts) Windows (本文)
    系统概况 macinfo wininfo
    大文件 Spotlight mdfind + find 补漏 Windows 搜索索引 + 目录补漏 + 限时扫描
    进程占用 ps + top Get-Process 双采样
    网络信息 ifconfig / scutil / networksetup Get-NetRoute / Get-NetIPConfiguration
    IO 监控 nettop(进程网络)+ fs_usage(磁盘) 网卡统计 + Get-Counter(磁盘进程)

    环境要求

    • 系统:Windows 10 及以上
    • Shell:PowerShell 5.1 或 PowerShell 7+
    • 模块NetTCPIP(Win10 自带,供 netinfo 使用)
    • 权限:一般用户即可;netio 磁盘计数器在部分环境需管理员权限

    参考

    MP3