📎 配套资产 / Companion assets:本篇所有示例的可下载文件在仓库 配套资产/B19_模块化脚本/——Logging 模块 + 合并 CSV 函数 + 主流程脚本 + 调用示例,每个子目录都带中文 README 与 English README。 English: downloadable files for every example in this article live in 配套资产/B19_模块化脚本/ — a Logging module + merge-CSV function + main script + usage examples, each with a bilingual README.

【自动化】AI 生成模块化脚本与错误处理

摘要:用 AI 写出像样的模块化脚本,出错也不怕、还能查日志。


一、痛点引入

你大概率被这种事坑过:

  • 让 AI 给你吐了一段 PowerShell 脚本,两三百行"挤成一团",看着能跑;结果第三个文件出错,整个脚本"啪"地崩了,前面全白跑,还得从头再来。
  • 出错了只给你甩一行红字,你根本不知道是哪一个文件、哪一步炸的,只能挨个文件手动试。
  • 第二天领导又要同样的处理,你发现路径写死在代码第 80 行,改个文件夹名得在代码里翻半天。

本质就三件事没做:脚本没拆模块、没兜底错误、没写日志。这篇咱们就用"通义"当助手,写出一份"拆得开、兜得住、查得到"的 PowerShell 脚本——出错不崩、崩了能查、改起来不头疼。

先认几个词(后面就不解释了):

  • PowerShell=Windows 自带的"命令行加强版",专门干批量自动化的活,不用装就能用。
  • 函数 / 模块=把大脚本切成几块独立小功能,像乐高,哪块要改就改哪块。
  • try/catch=给脚本戴上安全带,出错也不崩,还能记下错在哪。
  • 日志=把脚本运行过程写进一个 txt,相当于脚本的"行车记录仪"。
  • 参数化=把会变的(路径、文件名)提到脚本开头用参数控制,改起来不碰代码主体。

二、目标产出

跟完这篇,你会拿到一份真实可运行的 PowerShell 脚本,干三件事:

  1. 把"报表"文件夹里所有 CSV 合并成一张总表(每张表多了"来源文件"列,方便溯源);
  2. 某个文件坏了(打不开、被占用)也不影响其他文件——try/catch 兜底,坏的那个记进日志,好的照常合并;
  3. 每一步成功 / 失败都写进日志文件,事后能查"谁成功了、谁失败了、为啥失败"。

而且脚本用 function 拆成三个模块、路径全用 param 参数化,你改路径不用动代码主体。

三、案例实战

咱们的套路是:先把需求翻译成给 AI 的提示词 → 拿到初稿 → 咱们改成"模块化 + 错误处理 + 日志"的成品 → 跑起来看效果

第一步:把需求翻译成给"通义"的提示词

打开通义(tongyi.aliyun.com 或通义千问 App),把下面这段直接贴进去。重点是"要求"写得越具体,AI 初稿越接近能用的样子:

我在 Windows 上用 PowerShell。请帮我写一个脚本:把"报表"文件夹里所有 CSV 合并成一张总表,字段相同。要求:
1)用 function 拆成几个模块(日志函数、单文件合并函数、主流程);
2)用 try/catch 给每个文件兜底,某个文件出错不要影响其他文件;
3)把每一步成功/失败都写进日志文件;
4)源文件夹、输出文件、日志文件路径都用 param 参数控制,方便我改;
5)代码要能直接在 Windows PowerShell 5.1 下运行,注释用中文。
请给出完整代码。

通义会吐一版。但 AI 初稿常踩两个坑:try 里没加 -ErrorAction Stop(导致出错不进 catch),以及中文注释没考虑编码(见第五节)。所以咱们拿初稿当底子,改成下面这份经过实跑验证的成品。

第二步:成品脚本(模块化 + try/catch + 日志 + 参数化)

把下面整段存成 合并报表.ps1(存的时候注意第五节第 1 条,中文要带 BOM):

powershell
<# B19 示例:批量合并 CSV(模块化 + try/catch + 日志 + 参数化) 适用:Windows PowerShell 5.1 / PowerShell 7 均可直接运行 用法:powershell -ExecutionPolicy Bypass -File 合并报表.ps1 -SourceFolder ".\报表" -OutputFile ".\总表.csv" -LogFile ".\运行日志.log" (用 PowerShell 7 的话,把命令里的 powershell 换成 pwsh 即可) #> # ========== 参数化入口(必须放在脚本最前面)========== param( [string]$SourceFolder = ".\报表", [string]$OutputFile = ".\总表.csv", [string]$LogFile = ".\运行日志.log" ) # 把日志路径存成脚本级变量,供下面的 Write-Log 使用 $script:LogFile = $LogFile # ========== 模块 1:日志函数 ========== # try/catch=给脚本戴上安全带,出错也不崩,还能记下错在哪 function Write-Log { param( [Parameter(Mandatory = $true)] [string]$Message, [ValidateSet("INFO", "WARN", "ERROR")] [string]$Level = "INFO" ) $time = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $line = "[" + $time + "][" + $Level + "] " + $Message # 同时写屏幕和日志文件;UTF8 避免中文乱码 Add-Content -Path $script:LogFile -Value $line -Encoding UTF8 Write-Host $line } # ========== 模块 2:合并单个 CSV ========== function Merge-OneCsv { param( [Parameter(Mandatory = $true)] [string]$SourceFile, [Parameter(Mandatory = $true)] [string]$OutputFile ) try { # ErrorAction Stop:让 Import-Csv 出错时"真的"进 catch,而不是仅警告 $rows = Import-Csv -Path $SourceFile -Encoding UTF8 -ErrorAction Stop # 给每行加"来源文件"列,方便事后溯源 $rows | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name "来源文件" -Value (Split-Path $SourceFile -Leaf) -Force } # -Append:把多个文件追加到同一张总表 $rows | Export-Csv -Path $OutputFile -Encoding UTF8 -NoTypeInformation -Append -ErrorAction Stop $count = ($rows | Measure-Object).Count Write-Log -Message ("已合并:$SourceFile($count 行)") -Level "INFO" } catch { # $_ 是 PowerShell 的"当前错误对象",里面装着出错细节 Write-Log -Message ("合并失败:$SourceFile —— " + $_.Exception.Message) -Level "ERROR" # 注意:这里不 throw,让脚本继续处理下一个文件 } } # ========== 模块 3:主流程 ========== Write-Log -Message "===== 合并任务开始 =====" -Level "INFO" Write-Log -Message ("源文件夹:$SourceFolder") -Level "INFO" # 源文件夹不存在就直接报错退出 if (-not (Test-Path -Path $SourceFolder)) { Write-Log -Message ("源文件夹不存在:$SourceFolder") -Level "ERROR" exit 1 } # 先清空旧总表,避免 -Append 把历史数据又叠上去 if (Test-Path -Path $OutputFile) { Remove-Item -Path $OutputFile -Force } # 逐个处理 CSV Get-ChildItem -Path $SourceFolder -Filter *.csv -File | ForEach-Object { Merge-OneCsv -SourceFile $_.FullName -OutputFile $OutputFile } Write-Log -Message "===== 合并任务结束 =====" -Level "INFO"

这段怎么读(三个模块一目了然):

  • 模块 1 Write-Log:专门负责"记一笔"。以后任何地方想留痕,调它就行,不用重复写文件操作。
  • 模块 2 Merge-OneCsv:只干一件事——合并"一个"CSV。关键是外面包了 try/catch:里头 Import-Csv 一旦出错(文件被占用、损坏),就跳到 catch 记一笔错误,不抛出、不中断,于是下一个文件照常处理。
  • 模块 3 主流程:先记开始、检查文件夹在不在、清掉旧总表,再用 Get-ChildItem 把"报表"里的 CSV 一个个丢给模块 2。开头那几行 param(...) 就是参数化——改路径只动这三行,不用翻代码。

第三步:跑起来,看效果

准备一个"报表"文件夹,里面放两个同字段的 CSV(比如 a.csvb.csv,都有"姓名,金额"两列)。然后三种方式任选其一运行:

  • 方式 A(最稳):文件资源管理器进到脚本所在目录,按住 Shift 右键空白处 → "在终端中打开" → 输入:
    powershell
    powershell -ExecutionPolicy Bypass -File .\合并报表.ps1 -SourceFolder ".\报表" -OutputFile ".\总表.csv" -LogFile ".\运行日志.log" # PowerShell 7 把 powershell 换成 pwsh
  • 方式 B(VS Code):装个 PowerShell 插件,打开脚本,右上角点"运行"。
  • 方式 C(ISE):开始菜单搜 "PowerShell ISE",打开脚本按 F5。

跑完会多两个文件:总表.csv(合并结果,带"来源文件"列)和 运行日志.log(每一步的成败记录)。想换文件夹?只改 -SourceFolder 的值,比如 -SourceFolder "D:\每日报表",不用动脚本主体——这就是参数化的爽点。

四、原理小结

模块化、错误处理、日志这三者是"铁三角":模块让脚本可读可改(一块坏了只修一块,不牵一发动全身);try/catch 让单点失败不扩散(一个文件炸了,其他照跑,脚本不死);日志让失败可溯源(事后打开 log 就知道谁失败、为啥失败)。AI 擅长按这个骨架吐初稿,但两个边界它常漏——错误没加 -ErrorAction Stop 就兜不住、中文没存 BOM 就乱码——这两处得咱们自己把关。剩下的,就是复用这套模板,把"合并 CSV"换成你自己的活儿。

五、避坑指南

  1. 脚本里有中文?一定存成"UTF-8 带 BOM"。 这是最容易栽的坑。PowerShell 5.1 默认按系统编码(中文系统是 GBK)读 .ps1,UTF-8 无 BOM 的中文会被当成乱码整体读入,于是冒出各种莫名其妙的解析错误(比如"Try 语句缺少自己的 Catch 或 Finally 块""缺少终结符"等,本文脚本就因此折腾过)。VS Code:点右下角编码 → "以编码保存" → 选 UTF-8 with BOM;或者干脆升级 PowerShell 7,它能认 UTF-8 无 BOM。最省事:中文注释能少就少,或只在字符串里用中文、别碰代码关键字。
  2. try 里务必加 -ErrorAction Stop PowerShell 很多命令出错只是"警告",默认不进 catch;只有加了 Stop,错误才真正被 try/catch 抓住。本文 Import-Csv 后面那个 -ErrorAction Stop 一个字都不能省。
  3. 写日志用 -Encoding UTF8,别用默认编码。 PowerShell 5.1 写文件默认不是 UTF-8,换台机器中文可能变乱码;Add-Content -Encoding UTF8 显式指定最稳。
  4. Export-Csv -Append 要 PowerShell 3.0+。 老环境(比如很旧的 Windows)没有 -Append,得改成先把所有行收集到变量、再一次性 Export-Csv,否则会覆盖而不是追加。
  5. 用 COM object 控 Excel,务必在 finallyQuit + 释放。 否则 Excel 进程卡在后台关不掉,越跑越多(详见第六节的片段)。

六、进阶延展

  • 回指 B17(怕黑框?AI 教你跑第一条命令):如果你还没配好环境、看到黑框就手心出汗,先回看 B17,把"能跑通第一条命令"搞定,本文的脚本才有地方落脚。

  • 回指 B17《用 AI 写出你的第一条 PowerShell 命令》:三查法、危险命令黑名单、执行策略的说明都在那篇——先把"跑命令不踩坑"的底子打牢,回来再看本文会更顺。

  • 下探 L4:让脚本自己定时跑。 把今天的 合并报表.ps1 接上 Windows"任务计划程序",设每天凌晨自动合并昨日报表,你睡醒直接看总表。或者干脆不写代码——Excel 里的 Power Query(数据 → 获取数据 → 从文件 → 从文件夹)点几下鼠标就能做"合并 CSV",是脚本的"无代码平替"。

  • COM object 深入(下一层基础):想用 PowerShell 直接驱动 Excel 批量填表、批量转格式,靠的是下面这段"遥控 Excel"的写法。它需要本机装了 Excel,逻辑是标准的,但本文环境无 Excel,未实跑——你本地有 Excel 时可以照抄:

    powershell
    # COM object=组件对象模型,大白话就是让 PowerShell 直接"遥控"已经装好的 Excel 程序 # 片段自带一个最简日志函数,照抄即可独立运行(不依赖前面脚本里的 Write-Log) function Write-Log { param([string]$Message, [string]$Level = "INFO") $line = "[" + (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "][$Level] " + $Message Write-Host $line } $excel = New-Object -ComObject Excel.Application $excel.Visible = $false # 后台跑,不弹窗 $excel.DisplayAlerts = $false # 别弹"是否保存"的确认框 try { $wb = $excel.Workbooks.Open("C:\报表\样例.xlsx") $ws = $wb.Worksheets.Item(1) $ws.Range("A1").Value2 = "已处理" # 把 A1 改成指定内容 $wb.Save() } catch { Write-Log -Message ("Excel 操作出错:" + $_.Exception.Message) -Level "ERROR" } finally { # finally=不管成功失败都必跑的"收尾",用来释放 Excel,否则进程卡后台 if ($wb) { $wb.Close($false) } $excel.Quit() # 逆序释放 COM 引用:先工作表、再工作簿、最后 Excel 进程 if ($ws) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ws) | Out-Null } if ($wb) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null } if ($excel) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null } }

小结一下:今天你拿到的是一套"可复用的脚本骨架"——把 Merge-OneCsv 里的活儿换成你自己的(重命名、转格式、发邮件都行),模块和日志原样留着,就得到一个既稳又查得到的自动化工具。