Skip to content

PowerShell:编写 CSV 校验与汇总任务 ​

本篇以PowerShell CSV 汇总为例;前置是PowerShell 7.4、UTF-8 category/count 输入与对象管道。源码与输入与下文配置共用同一份文件,示例未执行。

教学版本族:PowerShell 7.4 与 .NET 8 语义;Windows PowerShell 5.1 不获得编码、原生命令传参与模块兼容保证。核对日期2026-10-17,不称为2026最新版。示例未执行,配置与命令需按目标环境验收。

CSV 汇总契约 ​

PowerShell CSV 示例未执行。完整脚本读取UTF-8 CSV,字段固定为 category、count,最多1000条;分类是小写 ASCII 标识符,count 是0至1000000的十进制整数。聚合后输出 Category、Total 对象,不写原文件、不访问服务或网络。已有PowerShell7.4即可,无第三方模块。

完整对象转换源码 ​

PowerShell CSV 汇总先检查全部行,再分组和求和,拒绝额外表头列、空表或非法值。输入文件需由当前用户拥有、稳定且非链接/重解析点,脚本只做末端对象检查,不能防御同身份并发替换或恶意祖先目录。导入失败是输入错误,运行异常进入错误流;下面是完整脚本和固定输入。

powershell
#requires -Version 7.4
[CmdletBinding()]
param([Parameter(Mandatory)][string]$InputFile)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
try {
    $item = Get-Item -LiteralPath $InputFile -ErrorAction Stop
    if ($item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
        [Console]::Error.WriteLine('Expected a regular non-reparse file.')
        exit 2
    }
    $rows = @(Import-Csv -LiteralPath $item.FullName -Encoding utf8 -ErrorAction Stop)
    if ($rows.Count -lt 1 -or $rows.Count -gt 1000) {
        [Console]::Error.WriteLine('Expected 1 to 1000 rows.')
        exit 2
    }
    $columns = @($rows[0].PSObject.Properties.Name)
    if ($columns.Count -ne 2 -or $columns[0] -cne 'category' -or $columns[1] -cne 'count') {
        [Console]::Error.WriteLine('Expected category,count header.')
        exit 2
    }
    $valid = foreach ($row in $rows) {
        if ($row.category -cnotmatch '^[a-z][a-z0-9_-]{0,31}$' -or
            $row.count -cnotmatch '^(0|[1-9][0-9]{0,6})$' -or [long]$row.count -gt 1000000) {
            [Console]::Error.WriteLine('Invalid row; no report produced.')
            exit 2
        }
        [pscustomobject]@{ Category = $row.category; Count = [long]$row.count }
    }
    $result = @($valid | Group-Object Category | ForEach-Object {
        [pscustomobject]@{
            Category = $_.Name
            Total = [long](($_.Group | Measure-Object Count -Sum).Sum)
        }
    } | Sort-Object Category)
    $result
    exit 0
}
catch [System.Management.Automation.ItemNotFoundException] {
    [Console]::Error.WriteLine('Input not found.')
    exit 2
}
catch {
    [Console]::Error.WriteLine('CSV processing failed; no complete report produced.')
    exit 1
}
csv
category,count
alpha,2
beta,4
alpha,3
csv
category,count
alpha,-1
text
# Existing PowerShell 7.4 / .NET 8.
# Built-in Microsoft.PowerShell.Utility and Microsoft.PowerShell.Management.
# No external programs, modules, network, or services.

完整项目位于仓库 examples/m5-next-powershell/;README给出全部未来自跑命令与精确清理步骤,正文包含全部源码和输入。

markdown
# CSV 对象汇总(未执行)

完整文件:summarize.ps1、good.csv、bad.csv、requirements.txt、本说明。已有PowerShell7.4,内置Utility/Management模块;不安装依赖、不改变执行策略。Windows/Linux语义材料共用,未做原生运行验证;Windows5.1编码和原生参数不在保证内。

输入是当前用户拥有、稳定的普通文件,父目录不可被不可信用户改写。CSV按UTF-8读取,表头严格按顺序为category,count;分类限小写ASCII标识符,count为0–1000000十进制,记录1–1000行。Import-Csv是容错CSV读取器,这不是任意损坏CSV的严格语法验证器;只对其解析出的字段契约检查。数据不作为命令执行。

未来隔离自跑命令,本轮未执行:

```powershell
pwsh -NoProfile -File .\summarize.ps1 -InputFile .\good.csv
$LASTEXITCODE
pwsh -NoProfile -File .\summarize.ps1 -InputFile .\bad.csv
$LASTEXITCODE
```

good预期alpha/5、beta/4两个对象,退出0。bad负数、空表、表头不符或超限预期退出2,无成功汇总;缺路径退出2。意外I/O失败退出1。命令宿主可能在加载/参数绑定前失败,这与业务失败需要分别记录。

结果是对象,应在脚本完成后由调用层导出或显示;不要先Format-Table再Export-Csv。文件中没有秘密、没有写操作、没有调度注册。I/O读取可能在内存校验前耗费较多资源,因此限受控小文件,不当成不可信超大文件防护器。

失败保留输入副本与退出码,检查权限/字段再重试;不自动修正负数或忽略额外列。恢复无需系统回滚。清理只涉及读者复制的本例文件与自行保存的输出,核对绝对路径后精确删除,不递归清理用户目录。

成功失败与展示 ​

PowerShell CSV 预期对 good.csv 输出两组:alpha总数5、beta总数4;读者可在末端 Format-Table 显示。bad.csv 包含负数,预期退出2,stdout 无汇总对象。若路径不存在同样退出2;意外读取错误退出1。显示顺序稳定不代表导出的字节和编码在5.1与7.4完全相同。

对象报告练习与解答 ​

题目:为什么 Format-Table | Export-Csv 不能保存期待的业务列?如果只一行,怎样避免误把字符串长度当记录数?

参考解答:格式化后已是显示指令对象,应直接导出汇总对象;用数组收集导入结果并检查 Count,同时按字段名读取字符串值。

核对资料 ​

继续阅读