You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell foreach访问特殊字符属性及MSSQL数据写入问题

问题1:带特殊字符/空格的属性访问语法

PowerShell中直接用.访问属性时,属性名不能包含空格、特殊符号(%、括号等),否则会被解析成运算符触发报错。正确访问方式是将属性名用单/双引号包裹后接在点运算符后,或放在方括号内,示例:

# 错误写法(会触发%运算符缺值的报错)
$cpu_percent_1=$CPUPer.CPU %
$memory_1=$CPUPer.Memory (MB)

# 正确写法1:单引号包裹属性名接在点后
$cpu_percent_1 = $CPUPer.'CPU %'
$memory_1 = $CPUPer.'Memory (MB)'

# 正确写法2:方括号包裹属性名索引访问
$cpu_percent_1 = $CPUPer['CPU %']

优先用单引号包裹属性名,避免双引号下的变量意外解析。

问题2:现有脚本逻辑问题排查

当前脚本存在5个核心问题,直接运行无法正常入库:

  • 错误将格式化输出作为数据源:Format-Table返回的是用于控制台显示的格式对象,不是原始结构化进程数据,把它的结果赋值给$CPUPercent后,遍历根本取不到Name、CPU这些真实属性值。
  • 重复采集+变量覆盖:第一行定义完$CPUPercent(CPU占比计算属性)、$CPU(CPU计算属性)后,直接执行了一次Get-Process输出到控制台,第二行又重复执行一次Get-Process,还把Format-Table的结果赋值给$CPUPercent,直接覆盖了之前定义的计算属性,属于无效重复操作。
  • SQL拼接风险:直接用变量拼接INSERT语句,遇到进程名、描述中带单引号的内容会直接触发SQL语法错误,同时存在SQL注入风险。
  • 边界值未处理:刚启动的进程运行总时长可能为0,计算CPU占比时会触发除以0的错误,返回NaN无效值。
  • 入库效率极低:逐进程调用Invoke-Sqlcmd会反复创建数据库连接、提交单条语句,进程数量多时性能很差。
数据入库优化方案

核心优化点

  • 采集阶段移除所有Format-*命令,仅保留原始结构化数据,需要控制台展示时单独调用格式化输出
  • 新增进程运行时长判断,避免除以0错误
  • 统一数值精度,和MSSQL表字段类型匹配
  • 用SqlBulkCopy批量提交代替逐行字符串拼接,既避免语法错误,也能把入库效率提升10倍以上
  • 修正特殊字符属性的访问写法,处理描述为空的场景避免入库报错

优化后参考脚本

# 定义计算属性
$cpuCal = @{
    Name = 'CPU'
    Expression = { [Math]::Round($_.CPU, 2) }
}
$cpuPercentCal = @{
    Name = 'CPU %'
    Expression = {
        $totalSec = (New-TimeSpan -Start $_.StartTime).TotalSeconds
        # 避免除以0错误,进程运行时长小于1秒时CPU占比记为0
        if ($totalSec -lt 1) { return 0 }
        [Math]::Round(($_.CPU * 100 / $totalSec), 2)
    }
}
$memoryCal = @{
    Name = 'Memory (MB)'
    Expression = { [Math]::Round(($_.WorkingSet64 / 1MB), 2) }
}

# 采集原始进程数据(不要加Format-Table)
$processData = Get-Process | Select-Object -Property Name, $cpuCal, $cpuPercentCal, $memoryCal, Description | 
    Sort-Object -Property CPU -Descending

# 控制台展示采集结果(单独调用格式化)
$processData | Format-Table -AutoSize

# 数据库连接配置
$dbServer = 'KILIKOOD-PC\MSSQLSERVER,1433'
$dbName = 'Fantasy'
$dbUser = 'sa'
$dbPwd = 'test123'
$connStr = "Server=$dbServer;Database=$dbName;User Id=$dbUser;Password=$dbPwd;TrustServerCertificate=True;"

# 用SqlBulkCopy批量入库
$bulkCopy = New-Object System.Data.SqlClient.SqlBulkCopy($connStr)
$bulkCopy.DestinationTableName = "[dbo].[ServiceTable]"
# 映射数据源字段和数据库表字段
$bulkCopy.ColumnMappings.Add("Name", "Name") | Out-Null
$bulkCopy.ColumnMappings.Add("CPU", "CPU") | Out-Null
$bulkCopy.ColumnMappings.Add("CPU %", "CPU_Perc") | Out-Null
$bulkCopy.ColumnMappings.Add("Memory (MB)", "Memory_MB") | Out-Null
$bulkCopy.ColumnMappings.Add("Description", "Description") | Out-Null

# 转成DataTable格式用于批量导入
$dataTable = New-Object System.Data.DataTable
$dataTable.Columns.Add("Name", [string]) | Out-Null
$dataTable.Columns.Add("CPU", [decimal]) | Out-Null
$dataTable.Columns.Add("CPU %", [decimal]) | Out-Null
$dataTable.Columns.Add("Memory (MB)", [decimal]) | Out-Null
$dataTable.Columns.Add("Description", [string]) | Out-Null

foreach ($proc in $processData) {
    $row = $dataTable.NewRow()
    $row["Name"] = $proc.Name
    $row["CPU"] = $proc.CPU
    $row["CPU %"] = $proc.'CPU %'
    $row["Memory (MB)"] = $proc.'Memory (MB)'
    $row["Description"] = if ($proc.Description) { $proc.Description } else { [DBNull]::Value }
    $dataTable.Rows.Add($row)
}

# 执行批量写入
$bulkCopy.WriteToServer($dataTable)
$bulkCopy.Close()

内容的提问来源于stack exchange,提问作者clodhead

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 09:42:30